diff --git a/Android.bp b/Android.bp index a90cb474026b2..938b18f3220b6 100644 --- a/Android.bp +++ b/Android.bp @@ -139,6 +139,9 @@ filegroup { ":vold_aidl", ":deviceproductinfoconstants_aidl", + ":adbrootservice_aidl", + ":lmofreeform_aidl", + // For the generated R.java and Manifest.java ":framework-res{.aapt.srcjar}", @@ -259,6 +262,7 @@ java_library { "com.android.sysprop.localization", "PlatformProperties", + "SurfaceFlingerProperties", ], sdk_version: "core_platform", installable: false, diff --git a/CleanSpec.mk b/CleanSpec.mk index 87535b3991ecc..d5fef59532550 100644 --- a/CleanSpec.mk +++ b/CleanSpec.mk @@ -78,6 +78,7 @@ $(call add-clean-step, rm -rf $(PRODUCT_OUT)/symbols/system/lib/libhwui.so) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/libhwui.so) $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/os/storage/*) $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/content/IClipboard.P) +$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/pocket/*) $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/telephony/java/com/android/internal/telephony/ITelephonyRegistry.P) $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/android_stubs_current_intermediates) $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/docs/api-stubs*) diff --git a/apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java b/apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java index 1fc888b06ffd3..dfd752075d118 100644 --- a/apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java +++ b/apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java @@ -74,6 +74,8 @@ void addPowerSaveTempWhitelistAppDirect(int uid, long duration, boolean isAppOnWhitelist(int appid); + int[] getPowerSaveWhitelistSystemAppIds(); + int[] getPowerSaveWhitelistUserAppIds(); int[] getPowerSaveTempWhitelistAppIds(); diff --git a/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java b/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java index ecb9a738aa324..dee7e61d4cc3b 100644 --- a/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java +++ b/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java @@ -111,6 +111,12 @@ public class AppStateTrackerImpl implements AppStateTracker { @GuardedBy("mLock") final SparseBooleanArray mActiveUids = new SparseBooleanArray(); + /** + * System exemption list in the device idle controller. + */ + @GuardedBy("mLock") + private int[] mPowerExemptSystemAppIds = new int[0]; + /** * System except-idle + user exemption list in the device idle controller. */ @@ -1046,6 +1052,7 @@ private void cleanUpArrayForUser(SparseBooleanArray array, int removedUserId) { * Called by device idle controller to update the power save exemption lists. */ public void setPowerSaveExemptionListAppIds( + int[] powerSaveExemptionListSystemAppIdArray, int[] powerSaveExemptionListExceptIdleAppIdArray, int[] powerSaveExemptionListUserAppIdArray, int[] tempExemptionListAppIdArray) { @@ -1053,6 +1060,7 @@ public void setPowerSaveExemptionListAppIds( final int[] previousExemptionList = mPowerExemptAllAppIds; final int[] previousTempExemptionList = mTempExemptAppIds; + mPowerExemptSystemAppIds = powerSaveExemptionListSystemAppIdArray; mPowerExemptAllAppIds = powerSaveExemptionListExceptIdleAppIdArray; mTempExemptAppIds = tempExemptionListAppIdArray; mPowerExemptUserAppIds = powerSaveExemptionListUserAppIdArray; @@ -1270,6 +1278,18 @@ public boolean isUidPowerSaveUserExempt(int uid) { } } + /** + * @return whether or not a UID is in either the user defined power-save exemption list or the + system full exemption list (not including except-idle) + */ + public boolean isUidPowerSaveIdleExempt(int uid) { + final int appId = UserHandle.getAppId(uid); + synchronized (mLock) { + return ArrayUtils.contains(mPowerExemptUserAppIds, appId) + || ArrayUtils.contains(mPowerExemptSystemAppIds, appId); + } + } + /** * @return whether a UID is in the temp power-save exemption list or not. * @@ -1305,6 +1325,9 @@ public void dump(IndentingPrintWriter pw) { pw.print("Active uids: "); dumpUids(pw, mActiveUids); + pw.print("System exemption list appids: "); + pw.println(Arrays.toString(mPowerExemptSystemAppIds)); + pw.print("Except-idle + user exemption list appids: "); pw.println(Arrays.toString(mPowerExemptAllAppIds)); @@ -1380,6 +1403,10 @@ public void dumpProto(ProtoOutputStream proto, long fieldId) { } } + for (int appId : mPowerExemptSystemAppIds) { + proto.write(AppStateTrackerProto.POWER_SAVE_SYSTEM_EXEMPT_APP_IDS, appId); + } + for (int appId : mPowerExemptAllAppIds) { proto.write(AppStateTrackerProto.POWER_SAVE_EXEMPT_APP_IDS, appId); } diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java index 1c37514dda0cd..4b29f71e07726 100644 --- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java +++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java @@ -581,6 +581,12 @@ static String lightStateToString(int state) { */ private final SparseBooleanArray mPowerSaveWhitelistSystemAppIds = new SparseBooleanArray(); + /** + * Current system app IDs that are in the complete power save white list. This array can + * be shared with others because it will not be modified once set. + */ + private int[] mPowerSaveWhitelistSystemAppIdArray = new int[0]; + /** * App IDs that have been white-listed to opt out of power save restrictions, except * for device idle modes. @@ -2388,6 +2394,11 @@ public String[] getFullPowerWhitelistExceptIdle() { return DeviceIdleController.this.getFullPowerWhitelistInternalUnchecked(); } + @Override + public int[] getPowerSaveWhitelistSystemAppIds() { + return DeviceIdleController.this.getPowerSaveWhitelistSystemAppIds(); + } + /** * Returns the array of app ids whitelisted by user. Take care not to * modify this, as it is a reference to the original copy. But the reference @@ -2618,6 +2629,12 @@ boolean isAppOnWhitelistInternal(int appid) { } } + int[] getPowerSaveWhitelistSystemAppIds() { + synchronized (this) { + return mPowerSaveWhitelistSystemAppIdArray; + } + } + int[] getPowerSaveWhitelistUserAppIds() { synchronized (this) { return mPowerSaveWhitelistUserAppIdArray; @@ -2628,6 +2645,16 @@ private static File getSystemDir() { return new File(Environment.getDataDirectory(), "system"); } + /** Returns the keys of a SparseBooleanArray, paying no attention to its values. */ + private static int[] keysToIntArray(final SparseBooleanArray sparseArray) { + final int size = sparseArray.size(); + final int[] array = new int[size]; + for (int i = 0; i < size; i++) { + array[i] = sparseArray.keyAt(i); + } + return array; + } + @Override public void onStart() { final PackageManager pm = getContext().getPackageManager(); @@ -2674,6 +2701,7 @@ public void onStart() { } catch (PackageManager.NameNotFoundException e) { } } + mPowerSaveWhitelistSystemAppIdArray = keysToIntArray(mPowerSaveWhitelistSystemAppIds); mConstants = mInjector.getConstants(this, mHandler, getContext().getContentResolver()); @@ -3203,9 +3231,11 @@ public int[] getAppIdTempWhitelistInternal() { void addPowerSaveTempAllowlistAppChecked(String packageName, long duration, int userId, @ReasonCode int reasonCode, @Nullable String reason) throws RemoteException { - getContext().enforceCallingOrSelfPermission( - Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST, - "No permission to change device idle whitelist"); + if (!packageName.equals("com.google.android.gms")) { + getContext().enforceCallingOrSelfPermission( + Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST, + "No permission to change device idle whitelist"); + } final int callingUid = Binder.getCallingUid(); userId = ActivityManager.getService().handleIncomingUser( Binder.getCallingPid(), @@ -4488,6 +4518,7 @@ private void reportTempWhitelistChangedLocked(final int uid, final boolean added private void passWhiteListsToForceAppStandbyTrackerLocked() { mAppStateTracker.setPowerSaveExemptionListAppIds( + mPowerSaveWhitelistSystemAppIdArray, mPowerSaveWhitelistExceptIdleAppIdArray, mPowerSaveWhitelistUserAppIdArray, mTempWhitelistAppIdArray); diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java index be06214cd841c..28da5ddcf79f0 100644 --- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java +++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java @@ -2737,7 +2737,7 @@ public void set(String callingPackage, } else if (workSource == null && (UserHandle.isCore(callingUid) || UserHandle.isSameApp(callingUid, mSystemUiUid) || ((mAppStateTracker != null) - && mAppStateTracker.isUidPowerSaveUserExempt(callingUid)))) { + && mAppStateTracker.isUidPowerSaveIdleExempt(callingUid)))) { flags |= FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED; flags &= ~(FLAG_ALLOW_WHILE_IDLE | FLAG_PRIORITIZE); } @@ -5559,7 +5559,7 @@ private void decrementAlarmCount(int uid, int decrement) { } } if (oldCount < decrement) { - Slog.wtf(TAG, "Attempt to decrement existing alarm count " + oldCount + " by " + Slog.w(TAG, "Attempt to decrement existing alarm count " + oldCount + " by " + decrement + " for uid " + uid); } } diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ComponentController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ComponentController.java index 9d4cba18b4b15..9220008e2a2fc 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/ComponentController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ComponentController.java @@ -157,6 +157,7 @@ private String getServiceProcessLocked(JobStatus jobStatus) { } ServiceInfo si; + boolean jobCleared = false; try { // createContextAsUser may potentially be expensive // TODO: cache user context or improve ContextImpl implementation if this becomes @@ -168,12 +169,16 @@ private String getServiceProcessLocked(JobStatus jobStatus) { if (mService.areUsersStartedLocked(jobStatus)) { // User is fully unlocked but PM still says the package doesn't exist. Slog.e(TAG, "Job exists for non-existent package: " + service.getPackageName()); + mService.getJobStore().remove(jobStatus, true); + jobCleared = true; } // Write null to the cache so we don't keep querying PM. si = null; } final String processName = si == null ? null : si.processName; - mServiceProcessCache.add(userId, service, processName); + if (!jobCleared) { + mServiceProcessCache.add(userId, service, processName); + } return processName; } diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/DeviceIdleJobsController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/DeviceIdleJobsController.java index 7f8f62675496d..71a2e2222fcb2 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/DeviceIdleJobsController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/DeviceIdleJobsController.java @@ -81,6 +81,7 @@ public final class DeviceIdleJobsController extends StateController { * True when in device idle mode, so we don't want to schedule any jobs. */ private boolean mDeviceIdleMode; + private int[] mPowerSaveWhitelistSystemAppIds; private int[] mDeviceIdleWhitelistAppIds; private int[] mPowerSaveTempWhitelistAppIds; @@ -145,6 +146,8 @@ public DeviceIdleJobsController(JobSchedulerService service) { mLocalDeviceIdleController = LocalServices.getService(DeviceIdleInternal.class); mDeviceIdleWhitelistAppIds = mLocalDeviceIdleController.getPowerSaveWhitelistUserAppIds(); + mPowerSaveWhitelistSystemAppIds = + mLocalDeviceIdleController.getPowerSaveWhitelistSystemAppIds(); mPowerSaveTempWhitelistAppIds = mLocalDeviceIdleController.getPowerSaveTempWhitelistAppIds(); mDeviceIdleUpdateFunctor = new DeviceIdleUpdateFunctor(); @@ -209,8 +212,9 @@ public void setUidActiveLocked(int uid, boolean active) { * Checks if the given job's scheduling app id exists in the device idle user whitelist. */ boolean isWhitelistedLocked(JobStatus job) { - return Arrays.binarySearch(mDeviceIdleWhitelistAppIds, - UserHandle.getAppId(job.getSourceUid())) >= 0; + final int appId = UserHandle.getAppId(job.getSourceUid()); + return Arrays.binarySearch(mDeviceIdleWhitelistAppIds, appId) >= 0 + || Arrays.binarySearch(mPowerSaveWhitelistSystemAppIds, appId) >= 0; } /** diff --git a/cmds/idmap2/idmap2d/Idmap2Service.cpp b/cmds/idmap2/idmap2d/Idmap2Service.cpp index 2495c55cc0656..e283f1c239a98 100644 --- a/cmds/idmap2/idmap2d/Idmap2Service.cpp +++ b/cmds/idmap2/idmap2d/Idmap2Service.cpp @@ -62,6 +62,7 @@ using PolicyBitmask = android::ResTable_overlayable_policy_header::PolicyBitmask namespace { constexpr std::string_view kFrameworkPath = "/system/framework/framework-res.apk"; +constexpr std::string_view kLineagePath = "/system/framework/org.lineageos.platform-res.apk"; Status ok() { return Status::ok(); @@ -242,9 +243,10 @@ Status Idmap2Service::createIdmap(const std::string& target_path, const std::str idmap2::Result Idmap2Service::GetTargetContainer( const std::string& target_path) { const bool is_framework = target_path == kFrameworkPath; + const bool is_lineage_framework = target_path == kLineagePath; bool use_cache; struct stat st = {}; - if (is_framework || !::stat(target_path.c_str(), &st)) { + if (is_framework || is_lineage_framework || !::stat(target_path.c_str(), &st)) { use_cache = true; } else { LOG(WARNING) << "failed to stat target path '" << target_path << "' for the cache"; diff --git a/core/api/current.txt b/core/api/current.txt index 0eddec03b327f..0e3e2d6f14080 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -13239,7 +13239,6 @@ package android.content.pm { method @Deprecated @Nullable public android.content.pm.PackageInstaller.SessionInfo getActiveStagedSession(); method @NonNull public java.util.List getActiveStagedSessions(); method @NonNull public java.util.List getAllSessions(); - method @FlaggedApi("android.content.pm.verification_service") @Nullable public final String getDeveloperVerificationServiceProvider(); method @NonNull public java.util.List getMySessions(); method @Nullable public android.content.pm.PackageInstaller.SessionInfo getSessionInfo(int); method @NonNull public java.util.List getStagedSessions(); @@ -13263,12 +13262,6 @@ package android.content.pm { field public static final String ACTION_SESSION_COMMITTED = "android.content.pm.action.SESSION_COMMITTED"; field public static final String ACTION_SESSION_DETAILS = "android.content.pm.action.SESSION_DETAILS"; field public static final String ACTION_SESSION_UPDATED = "android.content.pm.action.SESSION_UPDATED"; - field @FlaggedApi("android.content.pm.verification_service") public static final int DEVELOPER_VERIFICATION_FAILED_REASON_DEVELOPER_BLOCKED = 2; // 0x2 - field @FlaggedApi("android.content.pm.verification_service") public static final int DEVELOPER_VERIFICATION_FAILED_REASON_NETWORK_UNAVAILABLE = 1; // 0x1 - field @FlaggedApi("android.content.pm.verification_service") public static final int DEVELOPER_VERIFICATION_FAILED_REASON_UNKNOWN = 0; // 0x0 - field @FlaggedApi("android.content.pm.verification_service") public static final String EXTRA_DEVELOPER_VERIFICATION_EXTENSION_RESPONSE = "android.content.pm.extra.DEVELOPER_VERIFICATION_EXTENSION_RESPONSE"; - field @FlaggedApi("android.content.pm.verification_service") public static final String EXTRA_DEVELOPER_VERIFICATION_FAILURE_REASON = "android.content.pm.extra.DEVELOPER_VERIFICATION_FAILURE_REASON"; - field @FlaggedApi("android.content.pm.verification_service") public static final String EXTRA_DEVELOPER_VERIFICATION_LITE_PERFORMED = "android.content.pm.extra.DEVELOPER_VERIFICATION_LITE_PERFORMED"; field public static final String EXTRA_INSTALL_CONSTRAINTS = "android.content.pm.extra.INSTALL_CONSTRAINTS"; field public static final String EXTRA_INSTALL_CONSTRAINTS_RESULT = "android.content.pm.extra.INSTALL_CONSTRAINTS_RESULT"; field public static final String EXTRA_OTHER_PACKAGE_NAME = "android.content.pm.extra.OTHER_PACKAGE_NAME"; @@ -13458,7 +13451,6 @@ package android.content.pm { method @FlaggedApi("android.content.pm.sdk_dependency_installer") public void setAutoInstallDependenciesEnabled(boolean); method @Deprecated public void setAutoRevokePermissionsMode(boolean); method public void setDontKillApp(boolean); - method @FlaggedApi("android.content.pm.verification_service") public void setExtensionParams(@NonNull android.os.PersistableBundle); method public void setInstallLocation(int); method public void setInstallReason(int); method public void setInstallScenario(int); diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt index cd21d4b8589e5..886b936aaab04 100644 --- a/core/api/module-lib-current.txt +++ b/core/api/module-lib-current.txt @@ -272,12 +272,19 @@ package android.net { method @Nullable @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public android.telephony.SubscriptionPlan getSubscriptionPlan(@NonNull android.net.NetworkTemplate); method @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY) public boolean isUidNetworkingBlocked(int, boolean); method @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY) public boolean isUidRestrictedOnMeteredNetworks(int); + method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public void notifyDenylistChanged(@NonNull int[], @NonNull int[]); method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public void notifyStatsProviderLimitReached(); method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public void notifyStatsProviderWarningReached(); + method @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY) public void registerAllowedTransportsCallback(@Nullable java.util.concurrent.Executor, @NonNull android.net.NetworkPolicyManager.AllowedTransportsCallback); method @Deprecated @FlaggedApi("android.net.platform.flags.deprecate_network_policy_callback") @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY) public void registerNetworkPolicyCallback(@Nullable java.util.concurrent.Executor, @NonNull android.net.NetworkPolicyManager.NetworkPolicyCallback); + method @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY) public void unregisterAllowedTransportsCallback(@NonNull android.net.NetworkPolicyManager.AllowedTransportsCallback); method @Deprecated @FlaggedApi("android.net.platform.flags.deprecate_network_policy_callback") @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY) public void unregisterNetworkPolicyCallback(@NonNull android.net.NetworkPolicyManager.NetworkPolicyCallback); } + public static interface NetworkPolicyManager.AllowedTransportsCallback { + method public default void onUidsAllowedTransportsChanged(@NonNull int[], @NonNull long[]); + } + @Deprecated @FlaggedApi("android.net.platform.flags.deprecate_network_policy_callback") public static interface NetworkPolicyManager.NetworkPolicyCallback { method @Deprecated public default void onUidBlockedReasonChanged(int, int); } diff --git a/core/api/module-lib-lint-baseline.txt b/core/api/module-lib-lint-baseline.txt index 42c4efc139caa..6ebaf0cfeab86 100644 --- a/core/api/module-lib-lint-baseline.txt +++ b/core/api/module-lib-lint-baseline.txt @@ -1773,6 +1773,16 @@ UnflaggedApi: android.companion.CompanionDeviceManager.OnTransportsChangedListen New API must be flagged with @FlaggedApi: method android.companion.CompanionDeviceManager.OnTransportsChangedListener.onTransportsChanged(java.util.List) UnflaggedApi: android.content.Context#REMOTE_AUTH_SERVICE: New API must be flagged with @FlaggedApi: field android.content.Context.REMOTE_AUTH_SERVICE +UnflaggedApi: android.net.NetworkPolicyManager#notifyDenylistChanged(int[], int[]): + New API must be flagged with @FlaggedApi: method android.net.NetworkPolicyManager.notifyDenylistChanged(int[],int[]) +UnflaggedApi: android.net.NetworkPolicyManager#registerAllowedTransportsCallback(java.util.concurrent.Executor, android.net.NetworkPolicyManager.AllowedTransportsCallback): + New API must be flagged with @FlaggedApi: method android.net.NetworkPolicyManager.registerAllowedTransportsCallback(java.util.concurrent.Executor,android.net.NetworkPolicyManager.AllowedTransportsCallback) +UnflaggedApi: android.net.NetworkPolicyManager#unregisterAllowedTransportsCallback(android.net.NetworkPolicyManager.AllowedTransportsCallback): + New API must be flagged with @FlaggedApi: method android.net.NetworkPolicyManager.unregisterAllowedTransportsCallback(android.net.NetworkPolicyManager.AllowedTransportsCallback) +UnflaggedApi: android.net.NetworkPolicyManager.AllowedTransportsCallback: + New API must be flagged with @FlaggedApi: class android.net.NetworkPolicyManager.AllowedTransportsCallback +UnflaggedApi: android.net.NetworkPolicyManager.AllowedTransportsCallback#onUidsAllowedTransportsChanged(int[], long[]): + New API must be flagged with @FlaggedApi: method android.net.NetworkPolicyManager.AllowedTransportsCallback.onUidsAllowedTransportsChanged(int[],long[]) UnflaggedApi: android.os.IpcDataCache#MODULE_TELEPHONY: New API must be flagged with @FlaggedApi: field android.os.IpcDataCache.MODULE_TELEPHONY UnflaggedApi: android.provider.Settings.Config#getAllStrings(): diff --git a/core/api/system-current.txt b/core/api/system-current.txt index a0947b5d3ab09..51b253690d49d 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -63,7 +63,6 @@ package android { field public static final String BIND_CONTENT_CAPTURE_SERVICE = "android.permission.BIND_CONTENT_CAPTURE_SERVICE"; field public static final String BIND_CONTENT_SUGGESTIONS_SERVICE = "android.permission.BIND_CONTENT_SUGGESTIONS_SERVICE"; field @FlaggedApi("android.content.pm.sdk_dependency_installer") public static final String BIND_DEPENDENCY_INSTALLER = "android.permission.BIND_DEPENDENCY_INSTALLER"; - field @FlaggedApi("android.content.pm.verification_service") public static final String BIND_DEVELOPER_VERIFICATION_AGENT = "android.permission.BIND_DEVELOPER_VERIFICATION_AGENT"; field public static final String BIND_DIRECTORY_SEARCH = "android.permission.BIND_DIRECTORY_SEARCH"; field public static final String BIND_DISPLAY_HASHING_SERVICE = "android.permission.BIND_DISPLAY_HASHING_SERVICE"; field @FlaggedApi("com.android.internal.telephony.flags.use_oem_domain_selection_service") public static final String BIND_DOMAIN_SELECTION_SERVICE = "android.permission.BIND_DOMAIN_SELECTION_SERVICE"; @@ -146,7 +145,6 @@ package android { field public static final String CREATE_USERS = "android.permission.CREATE_USERS"; field public static final String CREATE_VIRTUAL_DEVICE = "android.permission.CREATE_VIRTUAL_DEVICE"; field public static final String CRYPT_KEEPER = "android.permission.CRYPT_KEEPER"; - field @FlaggedApi("android.content.pm.verification_service") public static final String DEVELOPER_VERIFICATION_AGENT = "android.permission.DEVELOPER_VERIFICATION_AGENT"; field public static final String DEVICE_POWER = "android.permission.DEVICE_POWER"; field public static final String DISABLE_SYSTEM_SOUND_EFFECTS = "android.permission.DISABLE_SYSTEM_SOUND_EFFECTS"; field public static final String DISPATCH_PROVISIONING_MESSAGE = "android.permission.DISPATCH_PROVISIONING_MESSAGE"; @@ -394,7 +392,6 @@ package android { field public static final String SET_ACTIVITY_WATCHER = "android.permission.SET_ACTIVITY_WATCHER"; field public static final String SET_CLIP_SOURCE = "android.permission.SET_CLIP_SOURCE"; field public static final String SET_DEFAULT_ACCOUNT_FOR_CONTACTS = "android.permission.SET_DEFAULT_ACCOUNT_FOR_CONTACTS"; - field @FlaggedApi("android.content.pm.verification_service") public static final String SET_DEVELOPER_VERIFICATION_USER_RESPONSE = "android.permission.SET_DEVELOPER_VERIFICATION_USER_RESPONSE"; field public static final String SET_HARMFUL_APP_WARNINGS = "android.permission.SET_HARMFUL_APP_WARNINGS"; field public static final String SET_LOW_POWER_STANDBY_PORTS = "android.permission.SET_LOW_POWER_STANDBY_PORTS"; field public static final String SET_MEDIA_KEY_LISTENER = "android.permission.SET_MEDIA_KEY_LISTENER"; @@ -4300,29 +4297,15 @@ package android.content.pm { } public class PackageInstaller { - method @FlaggedApi("android.content.pm.verification_service") @RequiresPermission(android.Manifest.permission.DEVELOPER_VERIFICATION_AGENT) public final int getDeveloperVerificationPolicy(); - method @FlaggedApi("android.content.pm.verification_service") @Nullable @RequiresPermission(android.Manifest.permission.SET_DEVELOPER_VERIFICATION_USER_RESPONSE) public android.content.pm.PackageInstaller.DeveloperVerificationUserConfirmationInfo getDeveloperVerificationUserConfirmationInfo(int); method @NonNull public android.content.pm.PackageInstaller.InstallInfo readInstallInfo(@NonNull java.io.File, int) throws android.content.pm.PackageInstaller.PackageParsingException; method @FlaggedApi("android.content.pm.read_install_info") @NonNull public android.content.pm.PackageInstaller.InstallInfo readInstallInfo(@NonNull android.os.ParcelFileDescriptor, @Nullable String, int) throws android.content.pm.PackageInstaller.PackageParsingException; - method @FlaggedApi("android.content.pm.verification_service") @RequiresPermission(android.Manifest.permission.DEVELOPER_VERIFICATION_AGENT) public final boolean setDeveloperVerificationPolicy(int); - method @FlaggedApi("android.content.pm.verification_service") @RequiresPermission(android.Manifest.permission.SET_DEVELOPER_VERIFICATION_USER_RESPONSE) public void setDeveloperVerificationUserResponse(int, int); method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void setPermissionsResult(int, boolean); field public static final String ACTION_CONFIRM_INSTALL = "android.content.pm.action.CONFIRM_INSTALL"; field public static final String ACTION_CONFIRM_PRE_APPROVAL = "android.content.pm.action.CONFIRM_PRE_APPROVAL"; field @FlaggedApi("android.content.pm.sdk_dependency_installer") public static final String ACTION_INSTALL_DEPENDENCY = "android.content.pm.action.INSTALL_DEPENDENCY"; - field @FlaggedApi("android.content.pm.verification_service") public static final String ACTION_NOTIFY_DEVELOPER_VERIFICATION_INCOMPLETE = "android.content.pm.action.NOTIFY_DEVELOPER_VERIFICATION_INCOMPLETE"; field public static final int DATA_LOADER_TYPE_INCREMENTAL = 2; // 0x2 field public static final int DATA_LOADER_TYPE_NONE = 0; // 0x0 field public static final int DATA_LOADER_TYPE_STREAMING = 1; // 0x1 - field @FlaggedApi("android.content.pm.verification_service") public static final int DEVELOPER_VERIFICATION_POLICY_BLOCK_FAIL_CLOSED = 3; // 0x3 - field @FlaggedApi("android.content.pm.verification_service") public static final int DEVELOPER_VERIFICATION_POLICY_BLOCK_FAIL_OPEN = 1; // 0x1 - field @FlaggedApi("android.content.pm.verification_service") public static final int DEVELOPER_VERIFICATION_POLICY_BLOCK_FAIL_WARN = 2; // 0x2 - field @FlaggedApi("android.content.pm.verification_service") public static final int DEVELOPER_VERIFICATION_POLICY_NONE = 0; // 0x0 - field @FlaggedApi("android.content.pm.verification_service") public static final int DEVELOPER_VERIFICATION_USER_RESPONSE_CANCEL = 1; // 0x1 - field @FlaggedApi("android.content.pm.verification_service") public static final int DEVELOPER_VERIFICATION_USER_RESPONSE_ERROR = 0; // 0x0 - field @FlaggedApi("android.content.pm.verification_service") public static final int DEVELOPER_VERIFICATION_USER_RESPONSE_INSTALL_ANYWAY = 4; // 0x4 - field @FlaggedApi("android.content.pm.verification_service") public static final int DEVELOPER_VERIFICATION_USER_RESPONSE_OK = 2; // 0x2 - field @FlaggedApi("android.content.pm.verification_service") public static final int DEVELOPER_VERIFICATION_USER_RESPONSE_RETRY = 3; // 0x3 field public static final String EXTRA_CALLBACK = "android.content.pm.extra.CALLBACK"; field public static final String EXTRA_DATA_LOADER_TYPE = "android.content.pm.extra.DATA_LOADER_TYPE"; field @FlaggedApi("android.content.pm.archiving") public static final String EXTRA_DELETE_FLAGS = "android.content.pm.extra.DELETE_FLAGS"; @@ -4336,20 +4319,6 @@ package android.content.pm { field public static final int REASON_REMIND_OWNERSHIP = 2; // 0x2 } - @FlaggedApi("android.content.pm.verification_service") public static final class PackageInstaller.DeveloperVerificationUserConfirmationInfo implements android.os.Parcelable { - ctor public PackageInstaller.DeveloperVerificationUserConfirmationInfo(); - ctor public PackageInstaller.DeveloperVerificationUserConfirmationInfo(int, int); - method public int describeContents(); - method public int getVerificationPolicy(); - method public int getVerificationUserActionNeededReason(); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator CREATOR; - field public static final int DEVELOPER_VERIFICATION_USER_ACTION_NEEDED_REASON_DEVELOPER_BLOCKED = 2; // 0x2 - field public static final int DEVELOPER_VERIFICATION_USER_ACTION_NEEDED_REASON_LITE_VERIFICATION = 3; // 0x3 - field public static final int DEVELOPER_VERIFICATION_USER_ACTION_NEEDED_REASON_NETWORK_UNAVAILABLE = 1; // 0x1 - field public static final int DEVELOPER_VERIFICATION_USER_ACTION_NEEDED_REASON_UNKNOWN = 0; // 0x0 - } - public static class PackageInstaller.InstallInfo { method public long calculateInstalledSize(@NonNull android.content.pm.PackageInstaller.SessionParams) throws java.io.IOException; method @FlaggedApi("android.content.pm.read_install_info") public long calculateInstalledSize(@NonNull android.content.pm.PackageInstaller.SessionParams, @NonNull android.os.ParcelFileDescriptor) throws java.io.IOException; @@ -4468,7 +4437,6 @@ package android.content.pm { method @Deprecated @RequiresPermission(android.Manifest.permission.INTENT_FILTER_VERIFICATION_AGENT) public abstract void verifyIntentFilter(int, int, @NonNull java.util.List); field public static final String ACTION_REQUEST_PERMISSIONS = "android.content.pm.action.REQUEST_PERMISSIONS"; field public static final String ACTION_REQUEST_PERMISSIONS_FOR_OTHER = "android.content.pm.action.REQUEST_PERMISSIONS_FOR_OTHER"; - field @FlaggedApi("android.content.pm.verification_service") public static final String ACTION_VERIFY_DEVELOPER = "android.content.pm.action.VERIFY_DEVELOPER"; field @FlaggedApi("android.content.pm.asl_in_apk_app_metadata_source") public static final int APP_METADATA_SOURCE_APK = 1; // 0x1 field @FlaggedApi("android.content.pm.asl_in_apk_app_metadata_source") public static final int APP_METADATA_SOURCE_INSTALLER = 2; // 0x2 field @FlaggedApi("android.content.pm.asl_in_apk_app_metadata_source") public static final int APP_METADATA_SOURCE_SYSTEM_IMAGE = 3; // 0x3 @@ -4745,64 +4713,6 @@ package android.content.pm.permission { } -package android.content.pm.verify.developer { - - @FlaggedApi("android.content.pm.verification_service") public final class DeveloperVerificationSession implements android.os.Parcelable { - method public int describeContents(); - method public long extendTimeRemaining(long); - method @NonNull public java.util.List getDeclaredLibraries(); - method @NonNull public android.os.PersistableBundle getExtensionParams(); - method public int getId(); - method public int getInstallSessionId(); - method @NonNull public String getPackageName(); - method @NonNull public android.content.pm.SigningInfo getSigningInfo(); - method @NonNull public android.net.Uri getStagedPackageUri(); - method public long getTimeoutTime(); - method public int getVerificationPolicy(); - method public void reportVerificationComplete(@NonNull android.content.pm.verify.developer.DeveloperVerificationStatus); - method public void reportVerificationComplete(@NonNull android.content.pm.verify.developer.DeveloperVerificationStatus, @NonNull android.os.PersistableBundle); - method public void reportVerificationIncomplete(int); - method public boolean setVerificationPolicy(int); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator CREATOR; - field public static final int DEVELOPER_VERIFICATION_INCOMPLETE_NETWORK_UNAVAILABLE = 1; // 0x1 - field public static final int DEVELOPER_VERIFICATION_INCOMPLETE_UNKNOWN = 0; // 0x0 - } - - @FlaggedApi("android.content.pm.verification_service") public final class DeveloperVerificationStatus implements android.os.Parcelable { - method public int describeContents(); - method public int getAslStatus(); - method @NonNull public String getFailureMessage(); - method public boolean isLite(); - method public boolean isVerified(); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator CREATOR; - field public static final int DEVELOPER_VERIFIER_STATUS_ASL_BAD = 2; // 0x2 - field public static final int DEVELOPER_VERIFIER_STATUS_ASL_GOOD = 1; // 0x1 - field public static final int DEVELOPER_VERIFIER_STATUS_ASL_UNDEFINED = 0; // 0x0 - } - - public static final class DeveloperVerificationStatus.Builder { - ctor public DeveloperVerificationStatus.Builder(); - method @NonNull public android.content.pm.verify.developer.DeveloperVerificationStatus build(); - method @NonNull public android.content.pm.verify.developer.DeveloperVerificationStatus.Builder setAslStatus(int); - method @NonNull public android.content.pm.verify.developer.DeveloperVerificationStatus.Builder setFailureMessage(@NonNull String); - method @NonNull public android.content.pm.verify.developer.DeveloperVerificationStatus.Builder setLite(boolean); - method @NonNull public android.content.pm.verify.developer.DeveloperVerificationStatus.Builder setVerified(boolean); - } - - @FlaggedApi("android.content.pm.verification_service") public abstract class DeveloperVerifierService extends android.app.Service { - ctor public DeveloperVerifierService(); - method @Nullable public android.os.IBinder onBind(@Nullable android.content.Intent); - method public abstract void onPackageNameAvailable(@NonNull String); - method public abstract void onVerificationCancelled(@NonNull String); - method public abstract void onVerificationRequired(@NonNull android.content.pm.verify.developer.DeveloperVerificationSession); - method public abstract void onVerificationRetry(@NonNull android.content.pm.verify.developer.DeveloperVerificationSession); - method public abstract void onVerificationTimeout(int); - } - -} - package android.content.pm.verify.domain { public final class DomainOwner implements android.os.Parcelable { @@ -12920,9 +12830,9 @@ package android.security.authenticationpolicy { field @FlaggedApi("android.security.secure_lockdown") public static final int ERROR_INVALID_PARAMS = 3; // 0x3 field @FlaggedApi("android.security.secure_lockdown") public static final int ERROR_NOT_AUTHORIZED = 7; // 0x7 field @FlaggedApi("android.security.secure_lockdown") public static final int ERROR_NO_BIOMETRICS_ENROLLED = 4; // 0x4 - field @FlaggedApi("android.security.secure_lockdown") public static final int ERROR_UNKNOWN = 1; // 0x1 + field @FlaggedApi("android.security.secure_lockdown") public static final int ERROR_UNKNOWN = 0; // 0x0 field @FlaggedApi("android.security.secure_lockdown") public static final int ERROR_UNSUPPORTED = 2; // 0x2 - field @FlaggedApi("android.security.secure_lockdown") public static final int SUCCESS = 0; // 0x0 + field @FlaggedApi("android.security.secure_lockdown") public static final int SUCCESS = 1; // 0x1 } @FlaggedApi("android.security.secure_lock_device") public static interface AuthenticationPolicyManager.SecureLockDeviceStatusListener { diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 33da82e21f43a..3e28164ed0843 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -1749,7 +1749,9 @@ package android.hardware.display { ctor public AmbientDisplayConfiguration(android.content.Context); method public boolean alwaysOnAvailable(); method public boolean alwaysOnAvailableForUser(int); + method public boolean alwaysOnChargingEnabledSetting(int); method public boolean alwaysOnEnabled(int); + method public boolean alwaysOnEnabledSetting(int); method public void disableDozeSettings(int); method public void disableDozeSettings(boolean, int); method public void restoreDozeSettings(int); diff --git a/core/api/test-lint-baseline.txt b/core/api/test-lint-baseline.txt index 8fb5905e8a554..60a9763dbdaf4 100644 --- a/core/api/test-lint-baseline.txt +++ b/core/api/test-lint-baseline.txt @@ -2045,6 +2045,10 @@ UnflaggedApi: android.content.pm.UserInfo#isPrivateProfile(): New API must be flagged with @FlaggedApi: method android.content.pm.UserInfo.isPrivateProfile() UnflaggedApi: android.credentials.CredentialProviderInfo#isPrimary(): New API must be flagged with @FlaggedApi: method android.credentials.CredentialProviderInfo.isPrimary() +UnflaggedApi: android.hardware.display.AmbientDisplayConfiguration#alwaysOnChargingEnabledSetting(int): + New API must be flagged with @FlaggedApi: method android.hardware.display.AmbientDisplayConfiguration.alwaysOnChargingEnabledSetting(int) +UnflaggedApi: android.hardware.display.AmbientDisplayConfiguration#alwaysOnEnabledSetting(int): + New API must be flagged with @FlaggedApi: method android.hardware.display.AmbientDisplayConfiguration.alwaysOnEnabledSetting(int) UnflaggedApi: android.hardware.input.InputManager#addUniqueIdAssociationByPort(String, String): New API must be flagged with @FlaggedApi: method android.hardware.input.InputManager.addUniqueIdAssociationByPort(String,String) UnflaggedApi: android.hardware.input.InputManager#removeUniqueIdAssociationByPort(String): diff --git a/core/java/android/adb/ADBRootService.java b/core/java/android/adb/ADBRootService.java new file mode 100644 index 0000000000000..52610eadad715 --- /dev/null +++ b/core/java/android/adb/ADBRootService.java @@ -0,0 +1,105 @@ +/* + * Copyright (C) 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. + */ + +package android.adb; + +import android.adbroot.IADBRootService; +import android.os.IBinder; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.util.Slog; + +/** + * {@hide} + */ +public class ADBRootService { + private static final String TAG = "ADBRootService"; + + private static final String ADB_ROOT_SERVICE = "adbroot_service"; + + private IADBRootService mService; + + private final IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() { + @Override + public void binderDied() { + if (mService != null) { + mService.asBinder().unlinkToDeath(this, 0); + } + mService = null; + } + }; + + private synchronized IADBRootService getService() + throws RemoteException { + if (mService != null) { + return mService; + } + + final IBinder service = ServiceManager.getService(ADB_ROOT_SERVICE); + if (service != null) { + service.linkToDeath(mDeathRecipient, 0); + mService = IADBRootService.Stub.asInterface(service); + return mService; + } + + Slog.e(TAG, "Unable to acquire ADBRootService"); + return null; + } + + /** + * @hide + */ + public boolean isSupported() { + try { + final IADBRootService svc = getService(); + if (svc != null) { + return svc.isSupported(); + } + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + return false; + } + + /** + * @hide + */ + public void setEnabled(boolean enable) { + try { + final IADBRootService svc = getService(); + if (svc != null) { + svc.setEnabled(enable); + } + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * @hide + */ + public boolean getEnabled() { + try { + final IADBRootService svc = getService(); + if (svc != null) { + return svc.getEnabled(); + } + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + return false; + } +} diff --git a/core/java/android/animation/Animator.java b/core/java/android/animation/Animator.java index fef97d382da16..ef709c094dd66 100644 --- a/core/java/android/animation/Animator.java +++ b/core/java/android/animation/Animator.java @@ -739,7 +739,7 @@ void callOnList( for (int i = 0; i < size; i++) { //noinspection unchecked T item = (T) array[i]; - call.call(item, animator, isReverse); + if (item != null) call.call(item, animator, isReverse); array[i] = null; } // Store it for the next call so we can reuse this array, if needed. diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index a54346bbbd2d4..5847a975e78de 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -244,6 +244,7 @@ import com.android.internal.os.SomeArgs; import com.android.internal.os.logging.MetricsLoggerWrapper; import com.android.internal.policy.DecorView; +import com.android.internal.util.android.FontController; import com.android.internal.util.ArrayUtils; import com.android.internal.util.FastPrintWriter; import com.android.internal.util.Preconditions; @@ -287,6 +288,9 @@ import java.util.Objects; import java.util.TimeZone; import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; @@ -402,6 +406,19 @@ public final class ActivityThread extends ClientTransactionHandler @UnsupportedAppUsage final H mH = new H(); final Executor mExecutor = new HandlerExecutor(mH); + + /** + * A single thread executor is sufficient, as we don't need to parallelize + * dumps within the same process. Using a single thread ensures dumps + * are processed in the order they are received. + */ + private static final ExecutorService sDumpExecutor = Executors.newSingleThreadExecutor( + new ThreadFactory() { + @Override + public Thread newThread(Runnable r) { + return new Thread(r, "dump-thread"); + } + }); /** * Maps from activity token to local record of running activities in this process. * @@ -1499,12 +1516,15 @@ public void dumpService(ParcelFileDescriptor pfd, IBinder servicetoken, String[] data.fd = pfd.dup(); data.token = servicetoken; data.args = args; - sendMessage(H.DUMP_SERVICE, data, 0, 0, true /*async*/); } catch (IOException e) { Slog.w(TAG, "dumpService failed", e); } finally { IoUtils.closeQuietly(pfd); } + // Submit the actual dump work to the dedicated executor. + sDumpExecutor.execute(() -> { + handleDumpService(data); + }); } // This function exists to make sure all receiver dispatching is @@ -1571,7 +1591,10 @@ public void dumpHeap(boolean managed, boolean mallocInfo, boolean runGc, String IoUtils.closeQuietly(fd); } dhd.finishCallback = finishCallback; - sendMessage(H.DUMP_HEAP, dhd, 0, 0, true /*async*/); + // Submit the actual dump work to the dedicated executor. + sDumpExecutor.execute(() -> { + handleDumpHeap(dhd); + }); } public void attachAgent(String agent) { @@ -1612,12 +1635,15 @@ public void dumpResources(ParcelFileDescriptor fd, RemoteCallback callback) { 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); } + // Submit the actual dump work to the dedicated executor. + sDumpExecutor.execute(() -> { + handleDumpResources(data); + }); } public void dumpActivity(ParcelFileDescriptor pfd, IBinder activitytoken, @@ -1628,12 +1654,15 @@ public void dumpActivity(ParcelFileDescriptor pfd, IBinder activitytoken, data.token = activitytoken; data.prefix = prefix; data.args = args; - sendMessage(H.DUMP_ACTIVITY, data, 0, 0, true /*async*/); } catch (IOException e) { Slog.w(TAG, "dumpActivity failed", e); } finally { IoUtils.closeQuietly(pfd); } + // Submit the actual dump work to the dedicated executor. + sDumpExecutor.execute(() -> { + handleDumpActivity(data); + }); } public void dumpProvider(ParcelFileDescriptor pfd, IBinder providertoken, @@ -1643,12 +1672,15 @@ public void dumpProvider(ParcelFileDescriptor pfd, IBinder providertoken, data.fd = pfd.dup(); data.token = providertoken; data.args = args; - sendMessage(H.DUMP_PROVIDER, data, 0, 0, true /*async*/); } catch (IOException e) { Slog.w(TAG, "dumpProvider failed", e); } finally { IoUtils.closeQuietly(pfd); } + // Submit the actual dump work to the dedicated executor. + sDumpExecutor.execute(() -> { + handleDumpProvider(data); + }); } @NeverCompile @@ -1995,12 +2027,15 @@ public void dumpGfxInfo(ParcelFileDescriptor pfd, String[] args) { data.fd = pfd.dup(); data.token = null; data.args = args; - sendMessage(H.DUMP_GFXINFO, data, 0, 0, true /*async*/); } catch (IOException e) { Slog.w(TAG, "dumpGfxInfo failed", e); } finally { IoUtils.closeQuietly(pfd); } + // Submit the actual dump work to the dedicated executor. + sDumpExecutor.execute(() -> { + handleDumpGfxInfo(data); + }); } @Override @@ -2434,7 +2469,6 @@ class H extends Handler { public static final int BIND_SERVICE = 121; @UnsupportedAppUsage public static final int UNBIND_SERVICE = 122; - public static final int DUMP_SERVICE = 123; public static final int LOW_MEMORY = 124; public static final int PROFILER_CONTROL = 127; public static final int CREATE_BACKUP_AGENT = 128; @@ -2445,8 +2479,6 @@ class H extends Handler { public static final int DISPATCH_PACKAGE_BROADCAST = 133; @UnsupportedAppUsage public static final int SCHEDULE_CRASH = 134; - public static final int DUMP_HEAP = 135; - public static final int DUMP_ACTIVITY = 136; public static final int SLEEPING = 137; public static final int SET_CORE_SETTINGS = 138; public static final int UPDATE_PACKAGE_COMPATIBILITY_INFO = 139; @@ -2472,8 +2504,6 @@ class H extends Handler { public static final int ATTACH_STARTUP_AGENTS = 162; 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 TIMEOUT_SERVICE = 167; public static final int PING = 168; @@ -2496,7 +2526,6 @@ String codeToString(int code) { case GC_WHEN_IDLE: return "GC_WHEN_IDLE"; case BIND_SERVICE: return "BIND_SERVICE"; case UNBIND_SERVICE: return "UNBIND_SERVICE"; - case DUMP_SERVICE: return "DUMP_SERVICE"; case LOW_MEMORY: return "LOW_MEMORY"; case PROFILER_CONTROL: return "PROFILER_CONTROL"; case CREATE_BACKUP_AGENT: return "CREATE_BACKUP_AGENT"; @@ -2505,8 +2534,6 @@ String codeToString(int code) { case REMOVE_PROVIDER: return "REMOVE_PROVIDER"; case DISPATCH_PACKAGE_BROADCAST: return "DISPATCH_PACKAGE_BROADCAST"; case SCHEDULE_CRASH: return "SCHEDULE_CRASH"; - case DUMP_HEAP: return "DUMP_HEAP"; - case DUMP_ACTIVITY: return "DUMP_ACTIVITY"; case SET_CORE_SETTINGS: return "SET_CORE_SETTINGS"; case UPDATE_PACKAGE_COMPATIBILITY_INFO: return "UPDATE_PACKAGE_COMPATIBILITY_INFO"; @@ -2528,11 +2555,9 @@ String codeToString(int code) { case UPDATE_UI_TRANSLATION_STATE: return "UPDATE_UI_TRANSLATION_STATE"; case SET_CONTENT_CAPTURE_OPTIONS_CALLBACK: return "SET_CONTENT_CAPTURE_OPTIONS_CALLBACK"; - case DUMP_GFXINFO: return "DUMP GFXINFO"; case INSTRUMENT_WITHOUT_RESTART: return "INSTRUMENT_WITHOUT_RESTART"; case FINISH_INSTRUMENTATION_WITHOUT_RESTART: return "FINISH_INSTRUMENTATION_WITHOUT_RESTART"; - case DUMP_RESOURCES: return "DUMP_RESOURCES"; case TIMEOUT_SERVICE: return "TIMEOUT_SERVICE"; case PING: return "PING"; case TIMEOUT_SERVICE_FOR_TYPE: return "TIMEOUT_SERVICE_FOR_TYPE"; @@ -2703,12 +2728,6 @@ public void handleMessage(Message msg) { Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); } break; - case DUMP_SERVICE: - handleDumpService((DumpComponentInfo)msg.obj); - break; - case DUMP_GFXINFO: - handleDumpGfxInfo((DumpComponentInfo) msg.obj); - break; case LOW_MEMORY: Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "lowMemory"); handleLowMemory(); @@ -2748,18 +2767,6 @@ public void handleMessage(Message msg) { throwRemoteServiceException(message, msg.arg1, extras); break; } - case DUMP_HEAP: - handleDumpHeap((DumpHeapData) msg.obj); - break; - case DUMP_RESOURCES: - handleDumpResources((DumpResourcesData) msg.obj); - break; - case DUMP_ACTIVITY: - handleDumpActivity((DumpComponentInfo)msg.obj); - break; - case DUMP_PROVIDER: - handleDumpProvider((DumpComponentInfo)msg.obj); - break; case SET_CORE_SETTINGS: Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "setCoreSettings"); handleSetCoreSettings((Bundle) msg.obj); @@ -4529,7 +4536,7 @@ private void reportSizeConfigurations(ActivityClientRecord r) { return; } Configuration[] configurations = r.activity.getResources().getSizeConfigurations(); - if (configurations == null) { + if (configurations == null || r.activity.mFinished) { return; } r.mSizeConfigurations = new SizeConfigurationBuckets(configurations); @@ -5360,7 +5367,7 @@ private void handleBindService(BindServiceData data) { Service s = mServices.get(data.token); if (DEBUG_SERVICE) Slog.v(TAG, "handleBindService s=" + s + " rebind=" + data.rebind); - if (s != null) { + if (s != null && createData != null) { try { data.intent.setExtrasClassLoader(s.getClassLoader()); data.intent.prepareToEnterProcess(isProtectedComponent(createData.info), @@ -5391,7 +5398,7 @@ private void handleBindService(BindServiceData data) { private void handleUnbindService(BindServiceData data) { CreateServiceData createData = mServicesData.get(data.token); Service s = mServices.get(data.token); - if (s != null) { + if (s != null && createData != null) { try { data.intent.setExtrasClassLoader(s.getClassLoader()); data.intent.prepareToEnterProcess(isProtectedComponent(createData.info), @@ -5499,7 +5506,7 @@ private void handleDumpProvider(DumpComponentInfo info) { private void handleServiceArgs(ServiceArgsData data) { CreateServiceData createData = mServicesData.get(data.token); Service s = mServices.get(data.token); - if (s != null) { + if (s != null && createData != null) { try { if (data.args != null) { data.args.setExtrasClassLoader(s.getClassLoader()); @@ -6947,6 +6954,8 @@ public void handleConfigurationChanged(Configuration config, int deviceId) { mConfigurationController.handleConfigurationChanged(config); updateDeviceIdForNonUIContexts(deviceId); + FontController.OnConfigurationChanged(getApplication().getResources()); + // These are only done to maintain @UnsupportedAppUsage and should be removed someday. mCurDefaultDisplayDpi = mConfigurationController.getCurDefaultDisplayDpi(); mConfiguration = mConfigurationController.getConfiguration(); @@ -7695,6 +7704,9 @@ private void handleBindApplication(AppBindData data) { data.info = getPackageInfo(data.appInfo, mCompatibilityInfo, null /* baseLoader */, false /* securityViolation */, true /* includeCode */, false /* registerPackage */, isSdkSandbox); + + FontController.OnConfigurationChanged(data.info.getResources()); + if (isSdkSandbox) { data.info.setSdkSandboxStorage(data.sdkSandboxClientAppVolumeUuid, data.sdkSandboxClientAppPackage); @@ -8287,11 +8299,6 @@ public IContentProvider acquireProvider( } } if (holder == null) { - if (UserManager.get(c).isUserUnlocked(userId)) { - Slog.e(TAG, "Failed to find provider info for " + auth); - } else { - Slog.w(TAG, "Failed to find provider info for " + auth + " (user not unlocked)"); - } return null; } @@ -8828,7 +8835,9 @@ private void attach(boolean system, long startSeq) { RuntimeInit.setApplicationObject(mAppThread.asBinder()); final IActivityManager mgr = ActivityManager.getService(); try { - mgr.attachApplication(mAppThread, startSeq); + if (mgr != null) { + mgr.attachApplication(mAppThread, startSeq); + } } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } @@ -8846,8 +8855,11 @@ private void attach(boolean system, long startSeq) { + " total=" + (runtime.totalMemory()/1024) + " used=" + (dalvikUsed/1024)); mSomeActivitiesChanged = false; + final IActivityTaskManager atmgr = ActivityTaskManager.getService(); try { - ActivityTaskManager.getService().releaseSomeActivities(mAppThread); + if (atmgr != null) { + atmgr.releaseSomeActivities(mAppThread); + } } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/content/pm/verify/developer/DeveloperVerificationStatus.aidl b/core/java/android/app/AppLockData.aidl similarity index 78% rename from core/java/android/content/pm/verify/developer/DeveloperVerificationStatus.aidl rename to core/java/android/app/AppLockData.aidl index 94f2db9e48e16..073d1efd25055 100644 --- a/core/java/android/content/pm/verify/developer/DeveloperVerificationStatus.aidl +++ b/core/java/android/app/AppLockData.aidl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2024 The Android Open Source Project + * Copyright (C) 2022 FlamingoOS Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,6 @@ * limitations under the License. */ -package android.content.pm.verify.developer; +package android.app; -/** @hide */ -parcelable DeveloperVerificationStatus; +parcelable AppLockData; \ No newline at end of file diff --git a/core/java/android/app/AppLockData.java b/core/java/android/app/AppLockData.java new file mode 100644 index 0000000000000..cb3352575c9cf --- /dev/null +++ b/core/java/android/app/AppLockData.java @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2022 FlamingoOS Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * Class to hold package level information about an + * application for app lock. + * + * @hide + */ +public final class AppLockData implements Parcelable { + + public static final Parcelable.Creator CREATOR = + new Parcelable.Creator() { + + @Override + public AppLockData createFromParcel(Parcel in) { + return new AppLockData(in); + } + + @Override + public AppLockData[] newArray(int size) { + return new AppLockData[size]; + } + }; + + private final String mPackageName; + private final boolean mShouldProtectApp; + private final boolean mShouldRedactNotification; + private final boolean mHideFromLauncher; + + /** @hide */ + public AppLockData( + @NonNull final String packageName, + final boolean shouldProtectApp, + final boolean shouldRedactNotification, + final boolean hideFromLauncher + ) { + mPackageName = packageName; + mShouldProtectApp = shouldProtectApp; + mShouldRedactNotification = shouldRedactNotification; + mHideFromLauncher = hideFromLauncher; + } + + private AppLockData(final Parcel in) { + mPackageName = in.readString(); + mShouldProtectApp = in.readBoolean(); + mShouldRedactNotification = in.readBoolean(); + mHideFromLauncher = in.readBoolean(); + } + + @NonNull + public String getPackageName() { + return mPackageName; + } + + public boolean getShouldProtectApp() { + return mShouldProtectApp; + } + + public boolean getShouldRedactNotification() { + return mShouldRedactNotification; + } + + public boolean getHideFromLauncher() { + return mHideFromLauncher; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(final Parcel parcel, final int flags) { + parcel.writeString(mPackageName); + parcel.writeBoolean(mShouldProtectApp); + parcel.writeBoolean(mShouldRedactNotification); + parcel.writeBoolean(mHideFromLauncher); + } + + @Override + @NonNull + public String toString() { + return "AppLockData[ packageName = " + mPackageName + + ", shouldProtectApp = " + mShouldProtectApp + + ", shouldRedactNotification = " + mShouldRedactNotification + + ", hideFromLauncher = " + mHideFromLauncher + " ]"; + } +} diff --git a/core/java/android/app/AppLockManager.java b/core/java/android/app/AppLockManager.java new file mode 100644 index 0000000000000..2fecb7260380c --- /dev/null +++ b/core/java/android/app/AppLockManager.java @@ -0,0 +1,281 @@ +/* + * Copyright (C) 2022 FlamingoOS Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app; + +import android.Manifest; +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.annotation.SystemService; +import android.annotation.RequiresPermission; +import android.annotation.UserHandleAware; +import android.content.Context; +import android.os.RemoteException; + +import java.util.List; + +/** + * @hide + */ +@SystemService(Context.APP_LOCK_SERVICE) +public final class AppLockManager { + + /** @hide */ + public static final long DEFAULT_TIMEOUT = 10 * 1000; + + /** @hide */ + public static final boolean DEFAULT_BIOMETRICS_ALLOWED = true; + + /** @hide */ + public static final boolean DEFAULT_PROTECT_APP = false; + + /** @hide */ + public static final boolean DEFAULT_REDACT_NOTIFICATION = false; + + /** @hide */ + public static final boolean DEFAULT_HIDE_IN_LAUNCHER = false; + + /** + * Intent action for starting credential activity in SystemUI. + * @hide + */ + public static final String ACTION_UNLOCK_APP = "android.app.action.UNLOCK_APP"; + + /** + * Intent extra to indicate whether usage of biometrics is allowed. + * @hide + */ + public static final String EXTRA_ALLOW_BIOMETRICS = "android.app.AppLockManager.ALLOW_BIOMETRICS"; + + /** + * Intent extra for the name of the application to unlock. + * @hide + */ + public static final String EXTRA_PACKAGE_LABEL = "android.app.AppLockManager.PACKAGE_LABEL"; + + private final Context mContext; + private final IAppLockManagerService mService; + + /** @hide */ + AppLockManager(Context context, IAppLockManagerService service) { + mContext = context; + mService = service; + } + + /** + * Set whether app should be protected by app lock + * in locked state. Caller must hold {@link android.permission.MANAGE_APP_LOCK}. + * + * @param packageName the package name. + * @param shouldProtectApp true to hide notification content. + * @hide + */ + @UserHandleAware + @RequiresPermission(Manifest.permission.MANAGE_APP_LOCK) + public void setShouldProtectApp(@NonNull String packageName, boolean shouldProtectApp) { + try { + mService.setShouldProtectApp(packageName, shouldProtectApp, mContext.getUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Get the current auto lock timeout. + * + * @param userId the user id given by the caller. + * @return the timeout in milliseconds if configuration for + * current user exists, -1 otherwise. + * @hide + */ + @UserHandleAware + public long getTimeout() { + try { + return mService.getTimeout(mContext.getUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Set auto lock timeout. + * Caller must hold {@link android.permission.MANAGE_APP_LOCK}. + * + * @param timeout the timeout in milliseconds. Must be >= 5. + * @param userId the user id given by the caller. + * @hide + */ + @UserHandleAware + @RequiresPermission(Manifest.permission.MANAGE_APP_LOCK) + public void setTimeout(long timeout) { + try { + mService.setTimeout(timeout, mContext.getUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Get all the packages protected with app lock. + * Caller must hold {@link android.permission.MANAGE_APP_LOCK}. + * + * @return a unique list of {@link AppLockData} of the protected apps. + * @hide + */ + @UserHandleAware + @NonNull + @RequiresPermission(Manifest.permission.MANAGE_APP_LOCK) + public List getPackageData() { + try { + return mService.getPackageData(mContext.getUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Set whether notification content should be redacted for a package + * in locked state. Caller must hold {@link android.permission.MANAGE_APP_LOCK}. + * + * @param packageName the package name. + * @param shouldRedactNotification true to hide notification content. + * @hide + */ + @UserHandleAware + @RequiresPermission(Manifest.permission.MANAGE_APP_LOCK) + public void setShouldRedactNotification(@NonNull String packageName, boolean shouldRedactNotification) { + try { + mService.setShouldRedactNotification(packageName, shouldRedactNotification, mContext.getUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Set whether to allow unlocking with biometrics. + * Caller must hold {@link android.permission.MANAGE_APP_LOCK}. + * + * @param biometricsAllowed whether to use biometrics. + * @hide + */ + @UserHandleAware + @RequiresPermission(Manifest.permission.MANAGE_APP_LOCK) + public void setBiometricsAllowed(boolean biometricsAllowed) { + try { + mService.setBiometricsAllowed(biometricsAllowed, mContext.getUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Check whether biometrics is allowed for unlocking. + * + * @return true if biometrics will be used for unlocking, false otherwise. + * @hide + */ + @UserHandleAware + public boolean isBiometricsAllowed() { + try { + return mService.isBiometricsAllowed(mContext.getUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Unlock a package following authentication with credentials. + * Caller must hold {@link android.permission.MANAGE_APP_LOCK}. + * + * @param packageName the name of the package to unlock. + * @hide + */ + @UserHandleAware + @RequiresPermission(Manifest.permission.MANAGE_APP_LOCK) + public void unlockPackage(@NonNull String packageName) { + try { + mService.unlockPackage(packageName, mContext.getUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Hide or unhide an application from launcher. + * Caller must hold {@link android.permission.MANAGE_APP_LOCK}. + * + * @param packageName the name of the package to hide or unhide. + * @param hide whether to hide or not. + * @hide + */ + @UserHandleAware + @RequiresPermission(Manifest.permission.MANAGE_APP_LOCK) + public void setPackageHidden(@NonNull String packageName, boolean hide) { + try { + mService.setPackageHidden(packageName, hide, mContext.getUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Get the list of applications hidden from launcher. + * Caller must hold {@link android.permission.MANAGE_APP_LOCK}. + * + * @return list of package names of the hidden apps. + * @hide + */ + @UserHandleAware + @RequiresPermission(Manifest.permission.MANAGE_APP_LOCK) + @NonNull + public List getHiddenPackages() { + try { + return mService.getHiddenPackages(mContext.getUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Check whether package is protected by app lock + * + * @return true if package is protected by app lock, false otherwise. + * @hide + */ + @UserHandleAware + public boolean isPackageProtected(@NonNull String packageName) { + try { + return mService.isPackageProtected(packageName, mContext.getUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Check whether package is hidden by app lock + * + * @return true if package is hidden by app lock, false otherwise. + * @hide + */ + @UserHandleAware + public boolean isPackageHidden(@NonNull String packageName) { + try { + return mService.isPackageHidden(packageName, mContext.getUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } +} diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index ec439a2286d72..09b651b965b0b 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -8440,13 +8440,13 @@ public boolean equals(@Nullable Object o) { } else { opCodes = null; } - final List result; try { - result = mService.getPackagesForOpsForDevice(opCodes, persistentDeviceId); + ParceledListSlice packageOps = mService.getPackagesForOpsForDevice(opCodes, + persistentDeviceId); + return packageOps == null ? Collections.emptyList() : packageOps.getList(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } - return (result != null) ? result : Collections.emptyList(); } /** @@ -8465,8 +8465,9 @@ public boolean equals(@Nullable Object o) { @UnsupportedAppUsage public List getPackagesForOps(int[] ops) { try { - return mService.getPackagesForOpsForDevice(ops, + ParceledListSlice packageOps = mService.getPackagesForOpsForDevice(ops, VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT); + return packageOps == null ? null : packageOps.getList(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index 4a6a23bd6f4e8..b42b34f2cb0fd 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -135,6 +135,8 @@ import com.android.internal.pm.RoSystemFeatures; import com.android.internal.util.UserIcons; +import com.nvidia.NvAppProfileService; + import dalvik.system.VMRuntime; import libcore.util.EmptyArray; @@ -189,6 +191,7 @@ public class ApplicationPackageManager extends PackageManager { private volatile UserManager mUserManager; private volatile PermissionManager mPermissionManager; private volatile PackageInstaller mInstaller; + private volatile NvAppProfileService mAppProfileService; private volatile ArtManager mArtManager; private volatile DevicePolicyManager mDevicePolicyManager; private volatile String mPermissionsControllerPackageName; @@ -512,6 +515,15 @@ public boolean isWirelessConsentModeEnabled() { com.android.internal.R.bool.config_wirelessConsentRequired); } + /** @hide */ + @Override + public NvAppProfileService getAppProfileService() { + if (mAppProfileService == null) { + mAppProfileService = new NvAppProfileService(mContext); + } + return mAppProfileService; + } + @Override public ApplicationInfo getApplicationInfo(String packageName, int flags) throws NameNotFoundException { @@ -824,8 +836,115 @@ public Boolean recompute(HasSystemFeatureQuery query) { } }; + private static final ArraySet PRIV_PKGS = new ArraySet<>(); + private static final ArraySet FEATURES_PIXEL = new ArraySet<>(); + private static final ArraySet FEATURES_PIXEL_OTHERS = new ArraySet<>(); + private static final ArraySet FEATURES_TENSOR = new ArraySet<>(); + private static final ArraySet FEATURES_NEXUS = new ArraySet<>(); + private static final ArraySet PTENSOR_CODENAMES = new ArraySet<>(); + private static final boolean IS_TENSOR_DEVICE; + + static { + Collections.addAll(FEATURES_PIXEL, + "com.google.android.apps.photos.PIXEL_2019_PRELOAD", + "com.google.android.apps.photos.PIXEL_2019_MIDYEAR_PRELOAD", + "com.google.android.apps.photos.PIXEL_2018_PRELOAD", + "com.google.android.apps.photos.PIXEL_2017_PRELOAD", + "com.google.android.feature.PIXEL_2021_MIDYEAR_EXPERIENCE", + "com.google.android.feature.PIXEL_2020_EXPERIENCE", + "com.google.android.feature.PIXEL_2020_MIDYEAR_EXPERIENCE", + "com.google.android.feature.PIXEL_2019_EXPERIENCE", + "com.google.android.feature.PIXEL_2019_MIDYEAR_EXPERIENCE", + "com.google.android.feature.PIXEL_2018_EXPERIENCE", + "com.google.android.feature.PIXEL_2017_EXPERIENCE", + "com.google.android.feature.PIXEL_EXPERIENCE", + "com.google.android.feature.GOOGLE_BUILD", + "com.google.android.feature.GOOGLE_EXPERIENCE" + ); + + Collections.addAll(FEATURES_PIXEL_OTHERS, + "com.google.android.feature.ASI", + "com.google.android.feature.ANDROID_ONE_EXPERIENCE", + "com.google.android.feature.GOOGLE_FI_BUNDLED", + "com.google.android.feature.LILY_EXPERIENCE", + "com.google.android.feature.TURBO_PRELOAD", + "com.google.android.feature.WELLBEING", + "com.google.lens.feature.IMAGE_INTEGRATION", + "com.google.lens.feature.CAMERA_INTEGRATION", + "com.google.photos.trust_debug_certs", + "com.google.android.feature.AER_OPTIMIZED", + "com.google.android.feature.NEXT_GENERATION_ASSISTANT", + "android.software.game_service", + "com.google.android.feature.EXCHANGE_6_2", + "com.google.android.apps.dialer.call_recording_audio", + "com.google.android.apps.dialer.SUPPORTED" + ); + + Collections.addAll(FEATURES_TENSOR, + "com.google.android.feature.PIXEL_2025_EXPERIENCE", + "com.google.android.feature.PIXEL_2025_MIDYEAR_EXPERIENCE", + "com.google.android.feature.PIXEL_2024_EXPERIENCE", + "com.google.android.feature.PIXEL_2024_MIDYEAR_EXPERIENCE", + "com.google.android.feature.PIXEL_2023_EXPERIENCE", + "com.google.android.feature.PIXEL_2023_MIDYEAR_EXPERIENCE", + "com.google.android.feature.PIXEL_2022_EXPERIENCE", + "com.google.android.feature.PIXEL_2022_MIDYEAR_EXPERIENCE", + "com.google.android.feature.PIXEL_2021_EXPERIENCE" + ); + + Collections.addAll(FEATURES_NEXUS, + "com.google.android.apps.photos.NEXUS_PRELOAD", + "com.google.android.apps.photos.nexus_preload", + "com.google.android.feature.PIXEL_EXPERIENCE", + "com.google.android.feature.GOOGLE_BUILD", + "com.google.android.feature.GOOGLE_EXPERIENCE" + ); + + Collections.addAll(PTENSOR_CODENAMES, + "blazer","frankel","mustang","tegu","comet","komodo","caiman","tokay", + "akita","husky","shiba","felix","tangorpro","lynx","cheetah","panther", + "bluejay","oriole","raven" + ); + + Collections.addAll(PRIV_PKGS, + "com.google.android.googlequicksearchbox", + "com.google.android.apps.photos", + "com.google.android.apps.pixel.agent", + "com.google.android.apps.pixel.creativeassistant" + ); + + final String device = SystemProperties.get("ro.product.device"); + IS_TENSOR_DEVICE = PTENSOR_CODENAMES.contains(device); + } + @Override public boolean hasSystemFeature(String name, int version) { + final String pkg = ActivityThread.currentPackageName(); + + if (name != null && pkg != null && PRIV_PKGS.contains(pkg)) { + final boolean photosSpoof = "com.google.android.apps.photos".equals(pkg) + && (Settings.Secure.getInt(mContext.getContentResolver(), + Settings.Secure.PI_PHOTOS_SPOOF, 1) == 1); + if (photosSpoof) { + if (FEATURES_PIXEL.contains(name)) return false; + if (FEATURES_PIXEL_OTHERS.contains(name)) return true; + if (FEATURES_TENSOR.contains(name)) return false; + if (FEATURES_NEXUS.contains(name)) return true; + } else { + if (FEATURES_PIXEL.contains(name)) return true; + if (FEATURES_PIXEL_OTHERS.contains(name)) return true; + if (FEATURES_TENSOR.contains(name)) return true; + if (FEATURES_NEXUS.contains(name)) return true; + } + } + + if (name != null && FEATURES_TENSOR.contains(name) && !IS_TENSOR_DEVICE) { + return false; + } + + if (name != null && FEATURES_PIXEL.contains(name)) return true; + if (name != null && FEATURES_PIXEL_OTHERS.contains(name)) return true; + // We check for system features in the following order: // * Build time-defined system features (constant, very efficient) // * SDK-defined system features (cached at process start, very efficient) diff --git a/core/java/android/app/ConfigurationController.java b/core/java/android/app/ConfigurationController.java index f491e3d274dba..3ed1263a71121 100644 --- a/core/java/android/app/ConfigurationController.java +++ b/core/java/android/app/ConfigurationController.java @@ -231,7 +231,7 @@ private void handleConfigurationChangedInner(@Nullable Configuration config, final int size = callbacks.size(); for (int i = 0; i < size; i++) { ComponentCallbacks2 cb = callbacks.get(i); - if (!equivalent) { + if (!equivalent && cb != null && config != null) { performConfigurationChanged(cb, config); } } @@ -282,6 +282,10 @@ int getCurDefaultDisplayDpi() { * original LocaleList. */ void updateLocaleListFromAppContext(@NonNull Context context) { + final Resources resources = context.getResources(); + if (resources == null) { + return; + } final Locale bestLocale = context.getResources().getConfiguration().getLocales().get(0); final LocaleList newLocaleList = mResourcesManager.getConfiguration().getLocales(); final int newLocaleListSize = newLocaleList.size(); diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 54a9b578c119c..d37f806876638 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -3757,6 +3757,11 @@ private File[] ensureExternalDirsExistOrFilter(File[] dirs, boolean tryCreateInP final File[] result = new File[dirs.length]; for (int i = 0; i < dirs.length; i++) { File dir = dirs[i]; + if (dir == null) { + // If input was null, we can't do anything + result[i] = null; + continue; + } if (!dir.exists()) { try { if (!tryCreateInProcess || !dir.mkdirs()) { @@ -3769,10 +3774,11 @@ private File[] ensureExternalDirsExistOrFilter(File[] dirs, boolean tryCreateInP } } catch (Exception e) { Log.w(TAG, "Failed to ensure " + dir, e); - dir = null; + // Do not null out the dir, so we can still return the path + // effectively preventing the NPE downstream } } - if (dir != null && !dir.canWrite()) { + if (!dir.canWrite()) { // Older versions of the MediaProvider mainline module had a rare early boot race // condition where app-private dirs could be created with the wrong permissions; // fix this up here. This check should be very fast, because dir.exists() above diff --git a/core/java/android/app/DownloadManager.java b/core/java/android/app/DownloadManager.java index f21c3e8d44d6e..e32c82b455afa 100644 --- a/core/java/android/app/DownloadManager.java +++ b/core/java/android/app/DownloadManager.java @@ -301,6 +301,13 @@ public class DownloadManager { */ public final static int PAUSED_UNKNOWN = 4; + /** + * Value of {@link #COLUMN_REASON} when the download is paused manually. + * + * @hide + */ + public final static int PAUSED_MANUAL = 5; + /** * Broadcast intent action sent by the download manager when a download completes. */ @@ -1000,6 +1007,7 @@ Cursor runQuery(ContentResolver resolver, String[] projection, Uri baseUri) { parts.add(statusClause("=", Downloads.Impl.STATUS_WAITING_TO_RETRY)); parts.add(statusClause("=", Downloads.Impl.STATUS_WAITING_FOR_NETWORK)); parts.add(statusClause("=", Downloads.Impl.STATUS_QUEUED_FOR_WIFI)); + parts.add(statusClause("=", Downloads.Impl.STATUS_PAUSED_MANUAL)); } if ((mStatusFlags & STATUS_SUCCESSFUL) != 0) { parts.add(statusClause("=", Downloads.Impl.STATUS_SUCCESS)); @@ -1294,6 +1302,34 @@ public void forceDownload(long... ids) { mResolver.update(mBaseUri, values, getWhereClauseForIds(ids), getWhereArgsForIds(ids)); } + /** + * Pause the given running download manually. + * + * @param id the ID of the download to be paused + * @return the number of downloads actually updated + * @hide + */ + public int pauseDownload(long id) { + ContentValues values = new ContentValues(); + values.put(Downloads.Impl.COLUMN_STATUS, Downloads.Impl.STATUS_PAUSED_MANUAL); + + return mResolver.update(ContentUris.withAppendedId(mBaseUri, id), values, null, null); + } + + /** + * Resume the given paused download manually. + * + * @param id the ID of the download to be resumed + * @return the number of downloads actually updated + * @hide + */ + public int resumeDownload(long id) { + ContentValues values = new ContentValues(); + values.put(Downloads.Impl.COLUMN_STATUS, Downloads.Impl.STATUS_RUNNING); + + return mResolver.update(ContentUris.withAppendedId(mBaseUri, id), values, null, null); + } + /** * Returns maximum size, in bytes, of downloads that may go over a mobile connection; or null if * there's no limit @@ -1784,6 +1820,9 @@ private long getPausedReason(int status) { case Downloads.Impl.STATUS_QUEUED_FOR_WIFI: return PAUSED_QUEUED_FOR_WIFI; + case Downloads.Impl.STATUS_PAUSED_MANUAL: + return PAUSED_MANUAL; + default: return PAUSED_UNKNOWN; } @@ -1839,6 +1878,7 @@ private int translateStatus(int status) { case Downloads.Impl.STATUS_WAITING_TO_RETRY: case Downloads.Impl.STATUS_WAITING_FOR_NETWORK: case Downloads.Impl.STATUS_QUEUED_FOR_WIFI: + case Downloads.Impl.STATUS_PAUSED_MANUAL: return STATUS_PAUSED; case Downloads.Impl.STATUS_SUCCESS: diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index 60be285dd2628..c98b5853511d9 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -1042,4 +1042,20 @@ interface IActivityManager { */ @EnforcePermission("INTERACT_ACROSS_USERS_FULL") IBinder refreshIntentCreatorToken(in Intent intent); + + /** + * Should disable touch if three fingers swipe enabled + */ + boolean isThreeFingersSwipeActive(); + void setThreeFingersSwipeActive(boolean active); + void setThreeGestureStateActive(boolean active); + + /** + * Force full screen for devices with cutout + */ + boolean shouldForceCutoutFullscreen(in String packageName); + + void releaseMemory(int minAdj, int maxKillCount, boolean includeUIProcesses, boolean skipCamera); + + void compactAllSystem(); } diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl index d8566b474b87b..4d7cadad14536 100644 --- a/core/java/android/app/IActivityTaskManager.aidl +++ b/core/java/android/app/IActivityTaskManager.aidl @@ -294,6 +294,11 @@ interface IActivityTaskManager { */ int getLastResumedActivityUserId(); + /** + * Return the uid of last resumed activity. + */ + int getLastResumedActivityUid(); + /** * Updates global configuration and applies changes to the entire system. * @param values Update values for global configuration. If null is passed it will request the diff --git a/core/java/android/app/IAppLockManagerService.aidl b/core/java/android/app/IAppLockManagerService.aidl new file mode 100644 index 0000000000000..6e14f4c86f17f --- /dev/null +++ b/core/java/android/app/IAppLockManagerService.aidl @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2022 FlamingoOS Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app; + +import android.app.AppLockData; + +/** + * Interface for managing app lock. + * @hide + */ +interface IAppLockManagerService { + + void setShouldProtectApp(in String packageName, in boolean secure, in int userId); + + long getTimeout(in int userId); + + void setTimeout(in long timeout, in int userId); + + List getPackageData(in int userId); + + void setShouldRedactNotification(in String packageName, in boolean secure, in int userId); + + void setBiometricsAllowed(in boolean biometricsAllowed, in int userId); + + boolean isBiometricsAllowed(in int userId); + + void unlockPackage(in String packageName, in int userId); + + void setPackageHidden(in String packageName, boolean hide, in int userId); + + List getHiddenPackages(in int userId); + + boolean isPackageProtected(in String packageName, in int userId); + + boolean isPackageHidden(in String packageName, in int userId); +} diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl index e6aad2d99d559..23d1090ed4ade 100644 --- a/core/java/android/app/INotificationManager.aidl +++ b/core/java/android/app/INotificationManager.aidl @@ -179,7 +179,6 @@ interface INotificationManager void setInterruptionFilter(String pkg, int interruptionFilter, boolean fromUser); NotificationChannel createConversationNotificationChannelForPackageFromPrivilegedListener(in INotificationListener token, String pkg, in UserHandle user, String parentChannelId, String conversationId); - void updateNotificationChannelGroupFromPrivilegedListener(in INotificationListener token, String pkg, in UserHandle user, in NotificationChannelGroup group); void updateNotificationChannelFromPrivilegedListener(in INotificationListener token, String pkg, in UserHandle user, in NotificationChannel channel); ParceledListSlice getNotificationChannelsFromPrivilegedListener(in INotificationListener token, String pkg, in UserHandle user); ParceledListSlice getNotificationChannelGroupsFromPrivilegedListener(in INotificationListener token, String pkg, in UserHandle user); diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java index d90db96819aa3..47ccffea5e3f9 100644 --- a/core/java/android/app/Instrumentation.java +++ b/core/java/android/app/Instrumentation.java @@ -78,6 +78,8 @@ import java.util.StringJoiner; import java.util.concurrent.TimeoutException; +import com.android.internal.util.crdroid.PixelPropsUtils; + /** * Base class for implementing application instrumentation code. When running * with instrumentation turned on, this class will be instantiated for you @@ -1349,6 +1351,7 @@ public Application newApplication(ClassLoader cl, String className, Context cont Application app = getFactory(context.getPackageName()) .instantiateApplication(cl, className); app.attach(context); + PixelPropsUtils.setProps(context); return app; } @@ -1366,6 +1369,7 @@ static public Application newApplication(Class clazz, Context context) ClassNotFoundException { Application app = (Application)clazz.newInstance(); app.attach(context); + PixelPropsUtils.setProps(context); return app; } diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java index a7cac2fd03976..4eeae21ea6918 100644 --- a/core/java/android/app/KeyguardManager.java +++ b/core/java/android/app/KeyguardManager.java @@ -1341,9 +1341,11 @@ private LockscreenCredential createLockscreenCredential( CharSequence pinStr = new String(password); return LockscreenCredential.createPin(pinStr); case PATTERN: + byte patternSize = new LockPatternUtils(mContext).getLockPatternSize( + mContext.getUserId()); List pattern = - LockPatternUtils.byteArrayToPattern(password); - return LockscreenCredential.createPattern(pattern); + LockPatternUtils.byteArrayToPattern(password, patternSize); + return LockscreenCredential.createPattern(pattern, patternSize); default: throw new IllegalArgumentException("Unknown lock type " + lockType); } diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java index 4fa6407d523a1..f6ccafbfeea45 100644 --- a/core/java/android/app/LoadedApk.java +++ b/core/java/android/app/LoadedApk.java @@ -1470,7 +1470,7 @@ private Application makeApplicationInner(boolean forceDefaultAppClass, false, false); for (int i = 0, n = packageIdentifiers.size(); i < n; i++) { final int id = packageIdentifiers.keyAt(i); - if (id == 0x01 || id == 0x7f) { + if (id == 0x01 || id == 0x7f || id == 0x3f) { continue; } diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 604d85dba4268..bf9a934c9c2ad 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -6296,13 +6296,6 @@ private void bindLargeIconAndApplyMargin(RemoteViews contentView, // If there is no title, the text (or big_text) needs to wrap around the image result.mTitleMarginSet.applyToView(contentView, p.mTextViewId); contentView.setInt(p.mTextViewId, "setNumIndentLines", p.hasTitle() ? 0 : 1); - } else if (notificationsRedesignTemplates()) { - // In the collapsed view, the top line needs to accommodate both the expander and - // large icon (when present) - result.mHeadingFullMarginSet.applyToView(contentView, R.id.notification_top_line); - // The text underneath can flow below the expander, but only if there's no large - // icon to leave space for (similar to the title in the expanded version). - result.mTitleMarginSet.applyToView(contentView, R.id.notification_main_column); } // The expand button uses paddings rather than margins, so we'll adjust it // separately. @@ -6417,7 +6410,7 @@ private void bindLargeIcon(RemoteViews contentView, @NonNull StandardTemplatePar contentView.setImageViewIcon(R.id.right_icon, rightIcon); contentView.setIntTag(R.id.right_icon, R.id.tag_keep_when_showing_left_icon, isPromotedPicture ? 1 : 0); - if (notificationsRedesignTemplates() || Flags.uiRichOngoing()) { + if ((notificationsRedesignTemplates() || Flags.uiRichOngoing()) && !p.mHeaderless) { contentView.setViewLayoutMargin(R.id.right_icon, RemoteViews.MARGIN_END, getLargeIconMarginEnd(p), COMPLEX_UNIT_PX); } @@ -6446,7 +6439,7 @@ int getLargeIconMarginEnd(@NonNull StandardTemplateParams p) { if (notificationsRedesignTemplates()) { int rightIconMarginPx = res.getDimensionPixelSize( - R.dimen.notification_2025_right_icon_margin_end); + R.dimen.notification_2025_right_icon_expanded_margin_end); int extraSpaceForExpanderPx = res.getDimensionPixelSize( R.dimen.notification_2025_extra_space_for_expander); return rightIconMarginPx + extraSpaceForExpanderPx; @@ -8413,16 +8406,12 @@ private static void buildCustomContentIntoTemplate(@NonNull Context context, customContent = customContent.clone(); if (p.mHeaderless) { template.removeFromParent(R.id.notification_top_line); - // We do not know how many lines a remote view has, so we presume it has 2; this + // We do not know how many lines ar emote view has, so we presume it has 2; this // ensures that we don't under-pad the content, which could lead to abuse, at the // cost of making single-line custom content over-padded. Builder.setHeaderlessVerticalMargins(template, p, true /* hasSecondLine */); - if (notificationsRedesignTemplates()) { - // also update the end margin to account for the large icon or expander - result.mHeadingFullMarginSet.applyToView(template, - R.id.notification_main_column); - } } else { + // also update the end margin to account for the large icon or expander Resources resources = context.getResources(); result.mTitleMarginSet.applyToView(template, R.id.notification_main_column, resources.getDimension(R.dimen.notification_content_margin_end) diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java index 0fbdbef1e3d9b..f8e119ef72ddb 100644 --- a/core/java/android/app/NotificationChannel.java +++ b/core/java/android/app/NotificationChannel.java @@ -651,7 +651,7 @@ private VibrationEffect getTrimmedVibrationEffect(VibrationEffect effect) { * @hide */ public void setId(String id) { - mId = id; + mId = getTrimmedString(id); } // Modifiable by apps on channel creation. @@ -895,8 +895,8 @@ public void setAllowBubbles(int allowed) { */ public void setConversationId(@NonNull String parentChannelId, @NonNull String conversationId) { - mParentId = parentChannelId; - mConversationId = conversationId; + mParentId = getTrimmedString(parentChannelId); + mConversationId = getTrimmedString(conversationId); } /** diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java index 927d469992843..105647bb6f775 100644 --- a/core/java/android/app/StatusBarManager.java +++ b/core/java/android/app/StatusBarManager.java @@ -272,6 +272,8 @@ public class StatusBarManager { public static final int CAMERA_LAUNCH_SOURCE_LIFT_TRIGGER = 2; /** @hide */ public static final int CAMERA_LAUNCH_SOURCE_QUICK_AFFORDANCE = 3; + /** @hide */ + public static final int CAMERA_LAUNCH_SOURCE_SCREEN_GESTURE = 4; /** * Broadcast action: sent to apps that hold the status bar permission when diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index 5bd84b03f1495..d9ac45c61b787 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -228,6 +228,8 @@ import android.permission.PermissionCheckerManager; import android.permission.PermissionControllerManager; import android.permission.PermissionManager; +import android.pocket.IPocketService; +import android.pocket.PocketManager; import android.print.IPrintManager; import android.print.PrintManager; import android.provider.E2eeContactKeysManager; @@ -258,7 +260,6 @@ import android.telephony.TelephonyFrameworkInitializer; import android.telephony.TelephonyRegistryManager; import android.transparency.BinaryTransparencyManager; -import android.util.ArrayMap; import android.util.Log; import android.util.Slog; import android.uwb.UwbFrameworkInitializer; @@ -295,6 +296,7 @@ import com.android.internal.policy.PhoneLayoutInflater; import com.android.internal.util.Preconditions; +import java.util.HashMap; import java.util.Map; import java.util.Objects; @@ -324,10 +326,10 @@ public final class SystemServiceRegistry { // Service registry information. // This information is never changed once static initialization has completed. private static final Map, String> SYSTEM_SERVICE_NAMES = - new ArrayMap, String>(); + new HashMap, String>(); private static final Map> SYSTEM_SERVICE_FETCHERS = - new ArrayMap>(); - private static final Map SYSTEM_SERVICE_CLASS_NAMES = new ArrayMap<>(); + new HashMap>(); + private static final Map SYSTEM_SERVICE_CLASS_NAMES = new HashMap<>(); private static int sServiceCacheSize; @@ -1060,6 +1062,18 @@ public AuthenticationPolicyManager createService(ContextImpl ctx) } }); + registerService(Context.POCKET_SERVICE, PocketManager.class, + new CachedServiceFetcher() { + @Override + public PocketManager createService(ContextImpl ctx) { + if (!ctx.getResources().getBoolean(R.bool.config_pocketModeSupported)) { + return null; + } + IBinder binder = ServiceManager.getService(Context.POCKET_SERVICE); + IPocketService service = IPocketService.Stub.asInterface(binder); + return new PocketManager(ctx.getOuterContext(), service); + }}); + registerService(Context.TV_INTERACTIVE_APP_SERVICE, TvInteractiveAppManager.class, new CachedServiceFetcher() { @Override @@ -1798,6 +1812,18 @@ public AdvancedProtectionManager createService(ContextImpl ctx) }); } + registerService(Context.APP_LOCK_SERVICE, AppLockManager.class, + new CachedServiceFetcher() { + @Override + public AppLockManager createService(ContextImpl ctx) + throws ServiceNotFoundException { + IBinder binder = ServiceManager.getServiceOrThrow( + Context.APP_LOCK_SERVICE); + return new AppLockManager(ctx, + IAppLockManagerService.Stub.asInterface(binder)); + } + }); + // DO NOT do a flag check like this unless the flag is read-only. // (because this code is executed during preload in zygote.) // If the flag is mutable, the check should be inside CachedServiceFetcher. diff --git a/core/java/android/app/admin/DeviceAdminInfo.java b/core/java/android/app/admin/DeviceAdminInfo.java index 8a8877f441a91..7b46db16f80a0 100644 --- a/core/java/android/app/admin/DeviceAdminInfo.java +++ b/core/java/android/app/admin/DeviceAdminInfo.java @@ -409,6 +409,8 @@ public DeviceAdminInfo(Context context, ActivityInfo activityInfo) } catch (NameNotFoundException e) { throw new XmlPullParserException( "Unable to create context for: " + mActivityInfo.packageName); + } catch (OutOfMemoryError e) { + throw new XmlPullParserException("Out of memory when parsing", null, e); } finally { if (parser != null) parser.close(); } diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 442944b73900b..26bf7d765fb0c 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -3745,7 +3745,7 @@ public void onInstallUpdateError( * Maximum supported password length. Kind-of arbitrary. * @hide */ - public static final int MAX_PASSWORD_LENGTH = 16; + public static final int MAX_PASSWORD_LENGTH = 64; /** * Service Action: Service implemented by a device owner or profile owner supervision app to @@ -18358,4 +18358,24 @@ public int getHeadlessDeviceOwnerMode() { } return HEADLESS_DEVICE_OWNER_MODE_UNSUPPORTED; } + + /** + * Lineage: check if secure keyguard is required + * @hide + */ + public boolean requireSecureKeyguard() { + return requireSecureKeyguard(UserHandle.myUserId()); + } + + /** @hide */ + public boolean requireSecureKeyguard(int userHandle) { + if (mService != null) { + try { + return mService.requireSecureKeyguard(userHandle); + } catch (RemoteException e) { + Log.w(TAG, "Failed to get secure keyguard requirement"); + } + } + return true; + } } diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index a3773412029de..768ab59d9dd3f 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -649,4 +649,6 @@ interface IDevicePolicyManager { void setAppFunctionsPolicy(String callerPackageName, int policy); int getAppFunctionsPolicy(String callerPackageName, int userId); + + boolean requireSecureKeyguard(int userHandle); } diff --git a/core/java/android/app/admin/SystemUpdateInfo.java b/core/java/android/app/admin/SystemUpdateInfo.java index 9e6c91f4ec313..7459b0e05e3af 100644 --- a/core/java/android/app/admin/SystemUpdateInfo.java +++ b/core/java/android/app/admin/SystemUpdateInfo.java @@ -133,7 +133,7 @@ public void writeToXml(TypedXmlSerializer out, String tag) throws IOException { out.startTag(null, tag); out.attributeLong(null, ATTR_RECEIVED_TIME, mReceivedTime); out.attributeInt(null, ATTR_SECURITY_PATCH_STATE, mSecurityPatchState); - out.attribute(null, ATTR_ORIGINAL_BUILD , Build.FINGERPRINT); + out.attribute(null, ATTR_ORIGINAL_BUILD , Build.VERSION.INCREMENTAL); out.endTag(null, tag); } @@ -142,7 +142,7 @@ public void writeToXml(TypedXmlSerializer out, String tag) throws IOException { public static SystemUpdateInfo readFromXml(TypedXmlPullParser parser) { // If an OTA has been applied (build fingerprint has changed), discard stale info. final String buildFingerprint = parser.getAttributeValue(null, ATTR_ORIGINAL_BUILD ); - if (!Build.FINGERPRINT.equals(buildFingerprint)) { + if (!Build.VERSION.INCREMENTAL.equals(buildFingerprint)) { return null; } try { diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java index c98aafc843277..36adfc8c96db7 100644 --- a/core/java/android/appwidget/AppWidgetHostView.java +++ b/core/java/android/appwidget/AppWidgetHostView.java @@ -16,6 +16,8 @@ package android.appwidget; +import static android.appwidget.AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY; +import static android.appwidget.AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD; import static android.appwidget.flags.Flags.FLAG_ENGAGEMENT_METRICS; import static android.appwidget.flags.Flags.engagementMetrics; import static android.content.res.Flags.selfTargetingAndroidResourceFrro; @@ -34,7 +36,6 @@ import android.content.pm.ApplicationInfo; import android.content.pm.LauncherActivityInfo; import android.content.pm.LauncherApps; -import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.Resources; import android.graphics.Canvas; import android.graphics.Color; @@ -53,7 +54,6 @@ import android.util.SparseArray; import android.util.SparseIntArray; import android.view.Gravity; -import android.view.LayoutInflater; import android.view.View; import android.view.ViewParent; import android.view.accessibility.AccessibilityNodeInfo; @@ -91,18 +91,6 @@ public class AppWidgetHostView extends FrameLayout implements AppWidgetHost.AppW static final int VIEW_MODE_ERROR = 2; static final int VIEW_MODE_DEFAULT = 3; - // Set of valid colors resources. - private static final int FIRST_RESOURCE_COLOR_ID = android.R.color.system_neutral1_0; - private static final int LAST_RESOURCE_COLOR_ID = android.R.color.system_accent3_1000; - - // When we're inflating the initialLayout for a AppWidget, we only allow - // views that are allowed in RemoteViews. - private static final LayoutInflater.Filter INFLATER_FILTER = - (clazz) -> clazz.isAnnotationPresent(RemoteViews.RemoteView.class); - - Context mContext; - Context mRemoteContext; - @UnsupportedAppUsage int mAppWidgetId; @UnsupportedAppUsage @@ -150,7 +138,6 @@ public AppWidgetHostView(Context context, InteractionHandler handler) { @SuppressWarnings({"UnusedDeclaration"}) public AppWidgetHostView(Context context, int animationIn, int animationOut) { super(context); - mContext = context; // We want to segregate the view ids within AppWidgets to prevent // problems when those ids collide with view ids in the AppWidgetHost. setIsRootNamespace(true); @@ -191,12 +178,6 @@ public static class AdapterChildHostView extends AppWidgetHostView { public AdapterChildHostView(Context context) { super(context); } - - @Override - public Context getRemoteContextEnsuringCorrectCachedApkPath() { - // To reduce noise in error messages - return null; - } } /** @@ -517,14 +498,11 @@ public void updateAppWidgetOptions(Bundle options) { AppWidgetManager.getInstance(mContext).updateAppWidgetOptions(mAppWidgetId, options); } - /** {@inheritDoc} */ + /** @hide **/ @Override - public LayoutParams generateLayoutParams(AttributeSet attrs) { - // We're being asked to inflate parameters, probably by a LayoutInflater - // in a remote Context. To help resolve any remote references, we - // inflate through our last mRemoteContext when it exists. - final Context context = mRemoteContext != null ? mRemoteContext : mContext; - return new FrameLayout.LayoutParams(context, attrs); + public LayoutParams generateLayoutParams(Context inflationContext, AttributeSet attrs) { + // Widget's layout parameter should be inflated in widget's context + return new FrameLayout.LayoutParams(inflationContext, attrs); } /** @@ -620,9 +598,6 @@ protected void applyRemoteViews(@Nullable RemoteViews remoteViews, boolean useAs inflateAsync(rvToApply); return; } - // Prepare a local reference to the remote Context so we're ready to - // inflate any requested LayoutParams. - mRemoteContext = getRemoteContextEnsuringCorrectCachedApkPath(); if (!mColorMappingChanged && rvToApply.canRecycleView(mView)) { try { @@ -683,7 +658,6 @@ private void applyContent(View content, boolean recycled, Exception exception) { private void inflateAsync(@NonNull RemoteViews remoteViews) { // Prepare a local reference to the remote Context so we're ready to // inflate any requested LayoutParams. - mRemoteContext = getRemoteContextEnsuringCorrectCachedApkPath(); int layoutId = remoteViews.getLayoutId(); if (mLastExecutionSignal != null) { @@ -782,30 +756,6 @@ public void onViewDataChanged(int viewId) { } } - /** - * Build a {@link Context} cloned into another package name, usually for the - * purposes of reading remote resources. - * - * @hide - */ - protected Context getRemoteContextEnsuringCorrectCachedApkPath() { - try { - Context newContext = mContext.createApplicationContext( - mInfo.providerInfo.applicationInfo, - Context.CONTEXT_RESTRICTED); - if (mColorResources != null) { - mColorResources.apply(newContext); - } - return newContext; - } catch (NameNotFoundException e) { - Log.e(TAG, "Package name " + mInfo.providerInfo.packageName + " not found"); - return mContext; - } catch (NullPointerException e) { - Log.e(TAG, "Error trying to create the remote context.", e); - return mContext; - } - } - /** * Prepare the given view to be shown. This might include adjusting * {@link FrameLayout.LayoutParams} before inserting. @@ -833,33 +783,28 @@ protected View getDefaultView() { Exception exception = null; try { - if (mInfo != null) { - Context theirContext = getRemoteContextEnsuringCorrectCachedApkPath(); - mRemoteContext = theirContext; - LayoutInflater inflater = (LayoutInflater) - theirContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); - inflater = inflater.cloneInContext(theirContext); - inflater.setFilter(INFLATER_FILTER); - AppWidgetManager manager = AppWidgetManager.getInstance(mContext); - Bundle options = manager.getAppWidgetOptions(mAppWidgetId); - + if (mInfo != null && mInfo.initialLayout != 0) { int layoutId = mInfo.initialLayout; - if (options.containsKey(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY)) { - int category = options.getInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY); - if (category == AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD) { - int kgLayoutId = mInfo.initialKeyguardLayout; - // If a default keyguard layout is not specified, use the standard - // default layout. - layoutId = kgLayoutId == 0 ? layoutId : kgLayoutId; + int kgLayoutId = mInfo.initialKeyguardLayout; + if (kgLayoutId != 0 && kgLayoutId != layoutId) { + // If this this a keyguard widget, use keyguard layout + Bundle options = AppWidgetManager.getInstance(mContext) + .getAppWidgetOptions(mAppWidgetId); + if (options.containsKey(OPTION_APPWIDGET_HOST_CATEGORY) + && options.getInt(OPTION_APPWIDGET_HOST_CATEGORY) + == WIDGET_CATEGORY_KEYGUARD) { + layoutId = kgLayoutId; } } - defaultView = inflater.inflate(layoutId, this, false); + + defaultView = new RemoteViewsWrapper(mInfo.providerInfo.applicationInfo, layoutId) + .apply(mContext, this, mInteractionLogger, null, mColorResources); if (!(defaultView instanceof AdapterView)) { // AdapterView does not support onClickListener defaultView.setOnClickListener(this::onDefaultViewClicked); } } else { - Log.w(TAG, "can't inflate defaultView because mInfo is missing"); + Log.w(TAG, "can't inflate defaultView, missing defaultLayout, mInfo: " + mInfo); } } catch (RuntimeException e) { exception = e; @@ -1243,5 +1188,13 @@ private AppWidgetEvent collectWidgetEvent() { return event; } } + + private static class RemoteViewsWrapper extends RemoteViews { + + RemoteViewsWrapper(ApplicationInfo application, int layoutId) { + super(application, layoutId); + } + } + } diff --git a/core/java/android/companion/AssociationRequest.java b/core/java/android/companion/AssociationRequest.java index fc6fe845bede6..f2c8a35fff967 100644 --- a/core/java/android/companion/AssociationRequest.java +++ b/core/java/android/companion/AssociationRequest.java @@ -318,6 +318,7 @@ private AssociationRequest( boolean forceConfirmation, boolean skipRoleGrant, @Nullable Icon deviceIcon) { + validateDisplayName(displayName); mSingleDevice = singleDevice; mDeviceFilters = requireNonNull(deviceFilters); mDeviceProfile = deviceProfile; @@ -431,6 +432,7 @@ public void setSkipPrompt(boolean value) { /** @hide */ public void setDisplayName(CharSequence displayName) { + validateDisplayName(displayName); mDisplayName = displayName; } @@ -522,11 +524,7 @@ public Builder setDeviceProfile(@NonNull @DeviceProfile String deviceProfile) { public Builder setDisplayName(@NonNull CharSequence displayName) { checkNotUsed(); mDisplayName = requireNonNull(displayName); - if (displayName.length() > DISPLAY_NAME_LENGTH_LIMIT) { - throw new IllegalArgumentException("Length of the display name must be at most " - + DISPLAY_NAME_LENGTH_LIMIT + " characters"); - } - + validateDisplayName(displayName); return this; } @@ -821,4 +819,11 @@ public AssociationRequest createFromParcel(@NonNull Parcel in) { return new AssociationRequest(in); } }; + + private static void validateDisplayName(@Nullable CharSequence displayName) { + if (displayName != null && displayName.length() > DISPLAY_NAME_LENGTH_LIMIT) { + throw new IllegalArgumentException("Length of the display name must be at most " + + DISPLAY_NAME_LENGTH_LIMIT + " characters"); + } + } } diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java index fa2e595b65ba1..5a76bf9e7515e 100644 --- a/core/java/android/content/ContentProvider.java +++ b/core/java/android/content/ContentProvider.java @@ -590,13 +590,14 @@ public ParcelFileDescriptor openFile(@NonNull AttributionSource attributionSourc throws FileNotFoundException { uri = validateIncomingUri(uri); uri = maybeGetUriWithoutUserId(uri); - enforceFilePermission(attributionSource, uri, mode); + final String updatedMode = validateFileMode(mode); + enforceFilePermission(attributionSource, uri, updatedMode); traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "openFile: ", uri.getAuthority()); final AttributionSource original = setCallingAttributionSource( attributionSource); try { return mInterface.openFile( - uri, mode, CancellationSignal.fromTransport(cancellationSignal)); + uri, updatedMode, CancellationSignal.fromTransport(cancellationSignal)); } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } finally { @@ -611,13 +612,14 @@ public AssetFileDescriptor openAssetFile(@NonNull AttributionSource attributionS throws FileNotFoundException { uri = validateIncomingUri(uri); uri = maybeGetUriWithoutUserId(uri); - enforceFilePermission(attributionSource, uri, mode); + final String updatedMode = validateFileMode(mode); + enforceFilePermission(attributionSource, uri, updatedMode); traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "openAssetFile: ", uri.getAuthority()); final AttributionSource original = setCallingAttributionSource( attributionSource); try { return mInterface.openAssetFile( - uri, mode, CancellationSignal.fromTransport(cancellationSignal)); + uri, updatedMode, CancellationSignal.fromTransport(cancellationSignal)); } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } finally { @@ -782,6 +784,25 @@ public boolean refresh(@NonNull AttributionSource attributionSource, Uri uri, } } + private String validateFileMode(String mode) { + // We currently only support the following modes: r, w, wt, wa, rw, rwt + // Note: ideally, we should check against the allowed modes and throw a + // SecurityException if the mode doesn't match any of them but to avoid app compat + // issues, we're silently dropping bits which allow modifying files when the write bit + // is not specified. + if (mode != null && mode.indexOf('w') == -1) { + // Don't allow truncation without write + if (mode.indexOf('t') != -1) { + mode = mode.replace("t", ""); + } + // Don't allow appending without write + if (mode.indexOf('a') != -1) { + mode = mode.replace("a", ""); + } + } + return mode; + } + @Override public int checkUriPermission(@NonNull AttributionSource attributionSource, Uri uri, int uid, int modeFlags) { diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 64affb91bdac6..e4c864dcf4623 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -50,6 +50,7 @@ import android.annotation.UserIdInt; import android.app.Activity; import android.app.ActivityManager; +import android.app.AppLockManager; import android.app.BroadcastOptions; import android.app.GameManager; import android.app.GrammaticalInflectionManager; @@ -6704,6 +6705,24 @@ public final T getSystemService(@NonNull Class serviceClass) { */ public static final String DISPLAY_HASH_SERVICE = "display_hash"; + /** + * {@link AppLockManager}. + * + * @see #getSystemService(String) + * @hide + */ + public static final String APP_LOCK_SERVICE = "app_lock"; + + /** + * Use with {@link #getSystemService} to retrieve a + * {@link android.os.PocketManager} for accessing and listening to device pocket state. + * + * @hide + * @see #getSystemService + * @see android.os.PocketManager + */ + public static final String POCKET_SERVICE = "pocket"; + /** * Use with {@link #getSystemService(String)} to retrieve a * {@link android.app.LocaleManager}. diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 05bfcc9bb86ce..0d7c77b5ceeb0 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -4526,6 +4526,13 @@ public static Intent createChooser(Intent target, CharSequence title, IntentSend */ public static final String ACTION_OVERLAY_CHANGED = "android.intent.action.OVERLAY_CHANGED"; + /** + * Broadcast Action: Display power state has changed. + * @hide + */ + public static final String ACTION_DISPLAY_STATE_CHANGED = + "android.intent.action.DISPLAY_STATE_CHANGED"; + /** * Activity Action: Allow the user to select and return one or more existing * documents. When invoked, the system will display the various diff --git a/core/java/android/content/pm/IPackageInstaller.aidl b/core/java/android/content/pm/IPackageInstaller.aidl index 26766913a522a..451c0e5e079ad 100644 --- a/core/java/android/content/pm/IPackageInstaller.aidl +++ b/core/java/android/content/pm/IPackageInstaller.aidl @@ -22,7 +22,6 @@ import android.content.pm.IPackageDeleteObserver2; import android.content.pm.IPackageInstallerCallback; import android.content.pm.IPackageInstallerSession; import android.content.pm.PackageInstaller; -import android.content.pm.PackageInstaller.DeveloperVerificationUserConfirmationInfo; import android.content.pm.ParceledListSlice; import android.content.pm.VersionedPackage; import android.content.IntentSender; @@ -94,16 +93,4 @@ interface IPackageInstaller { @JavaPassthrough(annotation="@android.annotation.RequiresPermission(anyOf={android.Manifest.permission.INSTALL_PACKAGES,android.Manifest.permission.REQUEST_INSTALL_PACKAGES})") void reportUnarchivalStatus(int unarchiveId, int status, long requiredStorageBytes, in PendingIntent userActionIntent, in UserHandle userHandle); - - @EnforcePermission("DEVELOPER_VERIFICATION_AGENT") - int getDeveloperVerificationPolicy(int userId); - @EnforcePermission("DEVELOPER_VERIFICATION_AGENT") - boolean setDeveloperVerificationPolicy(int policy, int userId); - String getDeveloperVerificationServiceProvider(); - - @EnforcePermission("SET_DEVELOPER_VERIFICATION_USER_RESPONSE") - void setDeveloperVerificationUserResponse(int sessionId, int developerVerificationUserResponse); - - @EnforcePermission("SET_DEVELOPER_VERIFICATION_USER_RESPONSE") - DeveloperVerificationUserConfirmationInfo getDeveloperVerificationUserConfirmationInfo(int sessionId); } diff --git a/core/java/android/content/pm/PackageInstaller.aidl b/core/java/android/content/pm/PackageInstaller.aidl index cbb11d55cab95..ab9d4f3194ca6 100644 --- a/core/java/android/content/pm/PackageInstaller.aidl +++ b/core/java/android/content/pm/PackageInstaller.aidl @@ -20,4 +20,3 @@ parcelable PackageInstaller.InstallConstraints; parcelable PackageInstaller.SessionParams; parcelable PackageInstaller.SessionInfo; parcelable PackageInstaller.PreapprovalDetails; -parcelable PackageInstaller.DeveloperVerificationUserConfirmationInfo; diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java index bcdc10c5cde8a..f733c47500134 100644 --- a/core/java/android/content/pm/PackageInstaller.java +++ b/core/java/android/content/pm/PackageInstaller.java @@ -61,8 +61,6 @@ import android.content.pm.parsing.PackageLite; import android.content.pm.parsing.result.ParseResult; import android.content.pm.parsing.result.ParseTypeImpl; -import android.content.pm.verify.developer.DeveloperVerificationSession; -import android.content.pm.verify.developer.DeveloperVerificationStatus; import android.content.pm.verify.domain.DomainSet; import android.graphics.Bitmap; import android.icu.util.ULocale; @@ -231,17 +229,6 @@ public class PackageInstaller { public static final String ACTION_INSTALL_DEPENDENCY = "android.content.pm.action.INSTALL_DEPENDENCY"; - /** - * Intent sent to the installer to indicate user action is required to proceed with the - * developer verification. - * - * @hide - */ - @SystemApi - @FlaggedApi(Flags.FLAG_VERIFICATION_SERVICE) - public static final String ACTION_NOTIFY_DEVELOPER_VERIFICATION_INCOMPLETE = - "android.content.pm.action.NOTIFY_DEVELOPER_VERIFICATION_INCOMPLETE"; - /** * An integer session ID that an operation is working with. * @@ -442,61 +429,6 @@ public class PackageInstaller { */ public static final String EXTRA_WARNINGS = "android.content.pm.extra.WARNINGS"; - /** - * When an installation fails because the developer verification was incomplete or blocked, - * this extra provides a code that explains the reason, such - * as {@link #DEVELOPER_VERIFICATION_FAILED_REASON_NETWORK_UNAVAILABLE}. It is included in the - * installation result returned via the {@link IntentSender} in - * {@link Session#commit(IntentSender)}. However, along with this reason code, installers can - * receive different status codes from {@link #EXTRA_STATUS} depending on their target SDK and - * privileged status: - *

- * Non-privileged installers targeting 36 or less will first receive the - * {@link #STATUS_PENDING_USER_ACTION} status code without this reason code. They will be - * forced through the user action flow to allow the OS to inform the user of such - * verification context before continuing to fail the install. If the user has the option - * to bypass the verification result and chooses to do so, the installation will proceed. - * Otherwise, the installer will receive the {@link #STATUS_FAILURE_ABORTED} status code - * along with this reason code that explains why the verification had failed. - *

- *

- * Privileged installer targeting 36 or less will directly receive the - * {@link #STATUS_FAILURE_ABORTED} status code. This is because they are not expected to - * have the capability of handling the {@link #STATUS_PENDING_USER_ACTION} flow, so the - * installation will directly fail. This reason code will be supplied to them for - * providing additional information. - *

- *

- * All installers targeting 37 and higher will receive a {@link #STATUS_FAILURE_ABORTED} - * status code along with this reason code, so the installers can explain the failure to the - * user accordingly. An {@link Intent#EXTRA_INTENT} will also be populated with an intent - * that can provide additional context where appropriate, should the installer prefer to - * defer to the OS to explain the failure to the user. - *

- */ - @FlaggedApi(Flags.FLAG_VERIFICATION_SERVICE) - public static final String EXTRA_DEVELOPER_VERIFICATION_FAILURE_REASON = - "android.content.pm.extra.DEVELOPER_VERIFICATION_FAILURE_REASON"; - - /** - * An extra containing the response provided by the developer verifier to any extension - * params provided by the installer. It will be of type {@link PersistableBundle}. - */ - @FlaggedApi(Flags.FLAG_VERIFICATION_SERVICE) - public static final String EXTRA_DEVELOPER_VERIFICATION_EXTENSION_RESPONSE = - "android.content.pm.extra.DEVELOPER_VERIFICATION_EXTENSION_RESPONSE"; - - /** - * An extra containing a boolean indicating whether the lite version of the developer - * verification was performed on the app to be installed. It is included in the installation - * result returned via the {@link IntentSender} in {@link Session#commit(IntentSender)} - * when the installation failed. - */ - @FlaggedApi(Flags.FLAG_VERIFICATION_SERVICE) - public static final String EXTRA_DEVELOPER_VERIFICATION_LITE_PERFORMED = - "android.content.pm.extra.DEVELOPER_VERIFICATION_LITE_PERFORMED"; - - /** * Streaming installation pending. * Caller should make sure DataLoader is able to prepare image and reinitiate the operation. @@ -840,149 +772,6 @@ public class PackageInstaller { @Retention(RetentionPolicy.SOURCE) public @interface UnarchivalStatus {} - /** - * Developer verification failed because of unknown reasons, such as when the verifier times out - * or cannot be connected. It can also corresponds to the status of - * {@link DeveloperVerificationSession#DEVELOPER_VERIFICATION_INCOMPLETE_UNKNOWN} reported by - * the verifier via {@link DeveloperVerificationSession#reportVerificationIncomplete(int)}. - */ - @FlaggedApi(Flags.FLAG_VERIFICATION_SERVICE) - public static final int DEVELOPER_VERIFICATION_FAILED_REASON_UNKNOWN = 0; - - /** - * Developer verification failed because the network is unavailable. This corresponds to the - * status of - * {@link DeveloperVerificationSession#DEVELOPER_VERIFICATION_INCOMPLETE_NETWORK_UNAVAILABLE} - * reported by the verifier via - * {@link DeveloperVerificationSession#reportVerificationIncomplete(int)}. - */ - @FlaggedApi(Flags.FLAG_VERIFICATION_SERVICE) - public static final int DEVELOPER_VERIFICATION_FAILED_REASON_NETWORK_UNAVAILABLE = 1; - - /** - * Developer verification failed because the developer cannot be verified, as reported by the - * verifier via - * {@link DeveloperVerificationSession#reportVerificationComplete(DeveloperVerificationStatus)} - * or - * {@link DeveloperVerificationSession#reportVerificationComplete( - * DeveloperVerificationStatus, PersistableBundle)} - */ - @FlaggedApi(Flags.FLAG_VERIFICATION_SERVICE) - public static final int DEVELOPER_VERIFICATION_FAILED_REASON_DEVELOPER_BLOCKED = 2; - - /** - * @hide - */ - @IntDef(value = { - DEVELOPER_VERIFICATION_FAILED_REASON_UNKNOWN, - DEVELOPER_VERIFICATION_FAILED_REASON_NETWORK_UNAVAILABLE, - DEVELOPER_VERIFICATION_FAILED_REASON_DEVELOPER_BLOCKED, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface DeveloperVerificationFailedReason { - } - - /** - * Do not block installs, regardless of developer verification status. - * @hide - */ - @FlaggedApi(Flags.FLAG_VERIFICATION_SERVICE) - @SystemApi - public static final int DEVELOPER_VERIFICATION_POLICY_NONE = 0; // platform default - /** - * Only block installations when the developer verification status says the developer is not - * verified, and ask the user if they'd like to install anyway when the verification cannot - * complete for any other reason. In case of a network issue, the user also has the option to - * retry the verification. - * @hide - */ - @FlaggedApi(Flags.FLAG_VERIFICATION_SERVICE) - @SystemApi - public static final int DEVELOPER_VERIFICATION_POLICY_BLOCK_FAIL_OPEN = 1; - /** - * Only block installations when the developer verification result says the developer is not - * verified, and ask the user if they'd like to install anyway when the verification cannot - * complete for any other reason. In case of a network issue, the user also has the option to - * retry the verification. - * @hide - */ - @FlaggedApi(Flags.FLAG_VERIFICATION_SERVICE) - @SystemApi - public static final int DEVELOPER_VERIFICATION_POLICY_BLOCK_FAIL_WARN = 2; - /** - * Block installations when the developer verification result says the developer is not verified - * or when the verification cannot be conducted because of unknown reasons. In case of a network - * issue, the user has the option to retry the verification. - * @hide - */ - @FlaggedApi(Flags.FLAG_VERIFICATION_SERVICE) - @SystemApi - public static final int DEVELOPER_VERIFICATION_POLICY_BLOCK_FAIL_CLOSED = 3; - /** - * @hide - */ - @IntDef(value = { - DEVELOPER_VERIFICATION_POLICY_NONE, - DEVELOPER_VERIFICATION_POLICY_BLOCK_FAIL_OPEN, - DEVELOPER_VERIFICATION_POLICY_BLOCK_FAIL_WARN, - DEVELOPER_VERIFICATION_POLICY_BLOCK_FAIL_CLOSED, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface DeveloperVerificationPolicy { - } - - /** - * This response code indicates that there was some error while showing a user confirmation - * dialog for developer verification. - * @hide - */ - @FlaggedApi(Flags.FLAG_VERIFICATION_SERVICE) - @SystemApi - public static final int DEVELOPER_VERIFICATION_USER_RESPONSE_ERROR = 0; - /** - * This indicates that the user has confirmed not to proceed with the installation as a response - * to the developer verification result. - * @hide - */ - @FlaggedApi(Flags.FLAG_VERIFICATION_SERVICE) - @SystemApi - public static final int DEVELOPER_VERIFICATION_USER_RESPONSE_CANCEL = 1; - /** - * This indicates that the user has acknowledged that installation cannot be completed due to - * a failed / incomplete developer verification. - * @hide - */ - @FlaggedApi(Flags.FLAG_VERIFICATION_SERVICE) - @SystemApi - public static final int DEVELOPER_VERIFICATION_USER_RESPONSE_OK = 2; - /** - * For an incomplete developer verification, the user has asked to retry the verification. - * @hide - */ - @FlaggedApi(Flags.FLAG_VERIFICATION_SERVICE) - @SystemApi - public static final int DEVELOPER_VERIFICATION_USER_RESPONSE_RETRY = 3; - /** - * For an incomplete developer verification, the user has confirmed proceeding with the - * installation anyway. - * @hide - */ - @FlaggedApi(Flags.FLAG_VERIFICATION_SERVICE) - @SystemApi - public static final int DEVELOPER_VERIFICATION_USER_RESPONSE_INSTALL_ANYWAY = 4; - /** - * @hide - */ - @IntDef(value = { - DEVELOPER_VERIFICATION_USER_RESPONSE_ERROR, - DEVELOPER_VERIFICATION_USER_RESPONSE_CANCEL, - DEVELOPER_VERIFICATION_USER_RESPONSE_OK, - DEVELOPER_VERIFICATION_USER_RESPONSE_RETRY, - DEVELOPER_VERIFICATION_USER_RESPONSE_INSTALL_ANYWAY, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface DeveloperVerificationUserResponse { - } /** Default set of checksums - includes all available checksums. * @see Session#requestChecksums */ @@ -1125,27 +914,6 @@ public void abandonSession(int sessionId) { } } - /** - * Returns the details about an incomplete or failed developer verification. Used by the default - * PackageInstaller app on the device to show appropriate informational dialogs to the user, - * when a user action is required. - * - * @return details for the requested session, or {@code null} if the session does not exist. - * - * @hide - */ - @FlaggedApi(Flags.FLAG_VERIFICATION_SERVICE) - @SystemApi - @RequiresPermission(android.Manifest.permission.SET_DEVELOPER_VERIFICATION_USER_RESPONSE) - public @Nullable DeveloperVerificationUserConfirmationInfo - getDeveloperVerificationUserConfirmationInfo(int sessionId) { - try { - return mInstaller.getDeveloperVerificationUserConfirmationInfo(sessionId); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - /** * Return list of all known install sessions, regardless of the installer. Callers need to * either declare <queries> element with the specific package name in the app's manifest, @@ -1746,75 +1514,6 @@ public void unregisterSessionCallback(@NonNull SessionCallback callback) { } } - /** - * Return the current developer verification enforcement policy. This may only be called by the - * package currently set by the system as the verifier agent. - * @hide - */ - @FlaggedApi(Flags.FLAG_VERIFICATION_SERVICE) - @SystemApi - @RequiresPermission(android.Manifest.permission.DEVELOPER_VERIFICATION_AGENT) - public final @DeveloperVerificationPolicy int getDeveloperVerificationPolicy() { - try { - return mInstaller.getDeveloperVerificationPolicy(mUserId); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** - * Set the current developer verification enforcement policy which will be applied to all future - * installation sessions. This may only be called by the package currently set by the system as - * the verifier agent. - * @hide - * @return whether the new policy was successfully set. - */ - @FlaggedApi(Flags.FLAG_VERIFICATION_SERVICE) - @SystemApi - @RequiresPermission(android.Manifest.permission.DEVELOPER_VERIFICATION_AGENT) - public final boolean setDeveloperVerificationPolicy(@DeveloperVerificationPolicy int policy) { - try { - return mInstaller.setDeveloperVerificationPolicy(policy, mUserId); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** - * Return the package name of the developer verification service provider, for the - * purpose of interacting with the specific verifier in relation to - * extension parameters and response structure. Return null if the system - * verifier service provider is not available to the caller, or if there is no - * such provider specified by the system. - */ - @FlaggedApi(Flags.FLAG_VERIFICATION_SERVICE) - public final @Nullable String getDeveloperVerificationServiceProvider() { - try { - return mInstaller.getDeveloperVerificationServiceProvider(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** - * Set user's response to an incomplete developer verification, regarding proceeding with the - * installation. - * - * @hide - */ - @FlaggedApi(Flags.FLAG_VERIFICATION_SERVICE) - @SystemApi - @RequiresPermission(android.Manifest.permission.SET_DEVELOPER_VERIFICATION_USER_RESPONSE) - public void setDeveloperVerificationUserResponse(int sessionId, - @DeveloperVerificationUserResponse int developerVerificationUserResponse) { - try { - mInstaller.setDeveloperVerificationUserResponse(sessionId, - developerVerificationUserResponse); - } catch (RemoteException e) { - e.rethrowFromSystemServer(); - } - } - /** * An installation that is being actively staged. For an install to succeed, * all existing and new packages must have identical package names, version @@ -3113,12 +2812,7 @@ public static class SessionParams implements Parcelable { /** {@hide} */ public @Nullable String dexoptCompilerFilter = null; /** {@hide} */ - public boolean forceVerification; - /** {@hide} */ public boolean isAutoInstallDependenciesEnabled = true; - /** {@hide} */ - @Nullable - public PersistableBundle extensionParams; private final ArrayMap mPermissionStates; @@ -3178,9 +2872,7 @@ public SessionParams(Parcel source) { developmentInstallFlags = source.readInt(); unarchiveId = source.readInt(); dexoptCompilerFilter = source.readString(); - forceVerification = source.readBoolean(); isAutoInstallDependenciesEnabled = source.readBoolean(); - extensionParams = source.readPersistableBundle(); } /** {@hide} */ @@ -3217,9 +2909,7 @@ public SessionParams copy() { ret.developmentInstallFlags = developmentInstallFlags; ret.unarchiveId = unarchiveId; ret.dexoptCompilerFilter = dexoptCompilerFilter; - ret.forceVerification = forceVerification; ret.isAutoInstallDependenciesEnabled = isAutoInstallDependenciesEnabled; - ret.extensionParams = extensionParams; return ret; } @@ -3960,14 +3650,6 @@ public String[] getLegacyGrantedRuntimePermissions() { return grantedPermissions.toArray(ArrayUtils.emptyArray(String.class)); } - /** - * Used by adb installations to force enable the verification for this install. - * {@hide} - */ - public void setForceVerification() { - this.forceVerification = true; - } - /** * Optionally indicate whether missing SDK or static shared library dependencies should be * automatically fetched and installed when installing an app that wants to use these @@ -3987,18 +3669,6 @@ public void setAutoInstallDependenciesEnabled(boolean enableAutoInstallDependenc isAutoInstallDependenciesEnabled = enableAutoInstallDependencies; } - /** - * Optionally called to provide a set of parameters to pass directly to the developer - * verification service provider (a.k.a., the verifier) to provide any additional context - * regarding the pending verification. The structure of this bundle will be specific to the - * implementation of the verifier, so callers can determine the verifier by calling - * {@link PackageInstaller#getDeveloperVerificationServiceProvider()}. - */ - @FlaggedApi(Flags.FLAG_VERIFICATION_SERVICE) - public void setExtensionParams(@NonNull PersistableBundle extensionParams) { - this.extensionParams = extensionParams; - } - /** {@hide} */ public void dump(IndentingPrintWriter pw) { pw.printPair("mode", mode); @@ -4034,9 +3704,7 @@ public void dump(IndentingPrintWriter pw) { pw.printHexPair("developmentInstallFlags", developmentInstallFlags); pw.printPair("unarchiveId", unarchiveId); pw.printPair("dexoptCompilerFilter", dexoptCompilerFilter); - pw.printPair("forceVerification", forceVerification); pw.printPair("isAutoInstallDependenciesEnabled", isAutoInstallDependenciesEnabled); - pw.printPair("extensionParams", extensionParams); pw.println(); } @@ -4083,9 +3751,7 @@ public void writeToParcel(Parcel dest, int flags) { dest.writeInt(developmentInstallFlags); dest.writeInt(unarchiveId); dest.writeString(dexoptCompilerFilter); - dest.writeBoolean(forceVerification); dest.writeBoolean(isAutoInstallDependenciesEnabled); - dest.writePersistableBundle(extensionParams); } public static final Parcelable.Creator @@ -5028,116 +4694,6 @@ public SessionInfo[] newArray(int size) { }; } - /** - * Details about an incomplete or failed developer verification that requires user intervention. - * - * @hide - */ - @FlaggedApi(Flags.FLAG_VERIFICATION_SERVICE) - @SystemApi - public static final class DeveloperVerificationUserConfirmationInfo implements Parcelable { - /** - * Developer verification requires user intervention because of unknown reasons, such as - * when the verifier times out or cannot be connected. - */ - public static final int DEVELOPER_VERIFICATION_USER_ACTION_NEEDED_REASON_UNKNOWN = 0; - - /** - * Developer verification requires user intervention because the network is unavailable. - */ - public static final int - DEVELOPER_VERIFICATION_USER_ACTION_NEEDED_REASON_NETWORK_UNAVAILABLE = 1; - - /** - * Developer verification requires user intervention because the developer is not verified. - */ - public static final int DEVELOPER_VERIFICATION_USER_ACTION_NEEDED_REASON_DEVELOPER_BLOCKED = - 2; - - /** - * Developer verification requires user intervention because only the lite version of the - * verification was completed on the request, not the full verification. - */ - public static final int DEVELOPER_VERIFICATION_USER_ACTION_NEEDED_REASON_LITE_VERIFICATION = - 3; - - /** - * @hide - */ - @IntDef(value = { - DEVELOPER_VERIFICATION_USER_ACTION_NEEDED_REASON_UNKNOWN, - DEVELOPER_VERIFICATION_USER_ACTION_NEEDED_REASON_NETWORK_UNAVAILABLE, - DEVELOPER_VERIFICATION_USER_ACTION_NEEDED_REASON_DEVELOPER_BLOCKED, - DEVELOPER_VERIFICATION_USER_ACTION_NEEDED_REASON_LITE_VERIFICATION - }) - @Retention(RetentionPolicy.SOURCE) - public @interface UserActionNeededReason { - } - - @DeveloperVerificationPolicy - private int mVerificationPolicy; - - @UserActionNeededReason - private int mVerificationUserActionNeededReason; - - public DeveloperVerificationUserConfirmationInfo() { - } - - public DeveloperVerificationUserConfirmationInfo(@DeveloperVerificationPolicy int policy, - @UserActionNeededReason int reason) { - mVerificationPolicy = policy; - mVerificationUserActionNeededReason = reason; - } - - private DeveloperVerificationUserConfirmationInfo(@NonNull Parcel in) { - mVerificationPolicy = in.readInt(); - mVerificationUserActionNeededReason = in.readInt(); - } - - @DeveloperVerificationPolicy - public int getVerificationPolicy() { - return mVerificationPolicy; - } - - @UserActionNeededReason - public int getVerificationUserActionNeededReason() { - return mVerificationUserActionNeededReason; - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeInt(mVerificationPolicy); - dest.writeInt(mVerificationUserActionNeededReason); - } - - public static final @NonNull Parcelable.Creator - CREATOR = new Parcelable.Creator<>() { - @Override - public DeveloperVerificationUserConfirmationInfo createFromParcel( - @NonNull Parcel p) { - return new DeveloperVerificationUserConfirmationInfo(p); - } - - @Override - public DeveloperVerificationUserConfirmationInfo[] newArray(int size) { - return new DeveloperVerificationUserConfirmationInfo[size]; - } - }; - - @Override - public String toString() { - return "VerificationUserConfirmationInfo{" - + "verificationPolicy=" + mVerificationPolicy - + ", verificationUserActionReason=" + mVerificationUserActionNeededReason - + '}'; - } - } - /** * Details for requesting the pre-commit install approval. */ @@ -5194,13 +4750,9 @@ public PreapprovalDetails( @Override public void writeToParcel(@NonNull Parcel dest, int flags) { byte flg = 0; - if (mIcon != null) { - flg |= 0x1; - } + if (mIcon != null) flg |= 0x1; dest.writeByte(flg); - if (mIcon != null) { - mIcon.writeToParcel(dest, flags); - } + if (mIcon != null) mIcon.writeToParcel(dest, flags); dest.writeCharSequence(mLabel); dest.writeString8(mLocale.toString()); dest.writeString8(mPackageName); @@ -5967,4 +5519,5 @@ long getRequiredStorageBytes() { return mUserActionIntent; } } + } diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 0c2a75177f5fc..bf2fce9fbf82b 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -63,7 +63,6 @@ import android.content.pm.dex.ArtManager; import android.content.pm.parsing.result.ParseResult; import android.content.pm.parsing.result.ParseTypeImpl; -import android.content.pm.verify.developer.DeveloperVerifierService; import android.content.pm.verify.domain.DomainVerificationManager; import android.content.res.Configuration; import android.content.res.Resources; @@ -110,6 +109,8 @@ import com.android.internal.util.ArrayUtils; import com.android.internal.util.DataClass; +import com.nvidia.NvAppProfileService; + import dalvik.system.VMRuntime; import java.io.File; @@ -5195,26 +5196,6 @@ default void onPermissionsChanged(int uid, @NonNull String persistentDeviceId) { public static final String ACTION_REQUEST_PERMISSIONS_FOR_OTHER = "android.content.pm.action.REQUEST_PERMISSIONS_FOR_OTHER"; - /** - * Used by the system to query a {@link DeveloperVerifierService} provider, - * which registers itself via an intent-filter handling this action. - * - *

Only the system can bind to the developer verifier service. This is protected - * by the {@link android.Manifest.permission#BIND_DEVELOPER_VERIFICATION_AGENT} permission. The - * developer verifier service app should protect the service by adding this permission in the - * service declaration in its manifest. - *

- * A developer verifier service must be a privileged app and hold the - * {@link android.Manifest.permission#DEVELOPER_VERIFICATION_AGENT} permission. - * - * @hide - */ - @SystemApi - @FlaggedApi(android.content.pm.Flags.FLAG_VERIFICATION_SERVICE) - @SdkConstant(SdkConstantType.SERVICE_ACTION) - public static final String ACTION_VERIFY_DEVELOPER = - "android.content.pm.action.VERIFY_DEVELOPER"; - /** * The names of the requested permissions. *

@@ -6290,6 +6271,11 @@ public abstract PermissionGroupInfo getPermissionGroupInfo(@NonNull String group public abstract List getAllPermissionGroups( @PermissionGroupInfoFlags int flags); + /** @hide */ + public NvAppProfileService getAppProfileService() { + return null; + } + /** * Get the platform-defined permissions which belong to a particular permission group. * diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 8614d5eb8b3fb..6de3848d06d19 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -57,6 +57,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.pm.overlay.OverlayPaths; +import android.content.pm.parsing.FrameworkParsingPackageUtils; import android.content.pm.parsing.result.ParseResult; import android.content.pm.parsing.result.ParseTypeImpl; import android.content.pm.permission.SplitPermissionInfoParcelable; @@ -1758,7 +1759,7 @@ private static ApkLite parseApkLite(String codePath, XmlPullParser parser, Attri } // Check to see if overlay should be excluded based on system property condition - if (!checkRequiredSystemProperties(requiredSystemPropertyName, + if (!FrameworkParsingPackageUtils.checkRequiredSystemProperties(requiredSystemPropertyName, requiredSystemPropertyValue)) { Slog.i(TAG, "Skipping target and overlay pair " + targetPackage + " and " + codePath + ": overlay ignored due to required system property: " @@ -2068,7 +2069,8 @@ private Package parseBaseApkCommon(Package pkg, Set acceptedTags, Resour } // check to see if overlay should be excluded based on system property condition - if (!checkRequiredSystemProperties(propName, propValue)) { + if (!FrameworkParsingPackageUtils.checkRequiredSystemProperties(propName, + propValue)) { Slog.i(TAG, "Skipping target and overlay pair " + pkg.mOverlayTarget + " and " + pkg.baseCodePath+ ": overlay ignored due to required system property: " + propName + " with value: " + propValue); @@ -2512,46 +2514,6 @@ private Package parseBaseApkCommon(Package pkg, Set acceptedTags, Resour return pkg; } - /** - * Returns {@code true} if both the property name and value are empty or if the given system - * property is set to the specified value. Properties can be one or more, and if properties are - * more than one, they must be separated by comma, and count of names and values must be equal, - * and also every given system property must be set to the corresponding value. - * In all other cases, returns {@code false} - */ - public static boolean checkRequiredSystemProperties(@Nullable String rawPropNames, - @Nullable String rawPropValues) { - if (TextUtils.isEmpty(rawPropNames) || TextUtils.isEmpty(rawPropValues)) { - if (!TextUtils.isEmpty(rawPropNames) || !TextUtils.isEmpty(rawPropValues)) { - // malformed condition - incomplete - Slog.w(TAG, "Disabling overlay - incomplete property :'" + rawPropNames - + "=" + rawPropValues + "' - require both requiredSystemPropertyName" - + " AND requiredSystemPropertyValue to be specified."); - return false; - } - // no valid condition set - so no exclusion criteria, overlay will be included. - return true; - } - - final String[] propNames = rawPropNames.split(","); - final String[] propValues = rawPropValues.split(","); - - if (propNames.length != propValues.length) { - Slog.w(TAG, "Disabling overlay - property :'" + rawPropNames - + "=" + rawPropValues + "' - require both requiredSystemPropertyName" - + " AND requiredSystemPropertyValue lists to have the same size."); - return false; - } - for (int i = 0; i < propNames.length; i++) { - // Check property value: make sure it is both set and equal to expected value - final String currValue = SystemProperties.get(propNames[i]); - if (!TextUtils.equals(currValue, propValues[i])) { - return false; - } - } - return true; - } - /** * This is a pre-density application which will get scaled - instead of being pixel perfect. * This type of application is not resizable. @@ -3637,11 +3599,7 @@ private boolean parseBaseApplication(Package owner, Resources res, ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_ALLOW_CLEAR_USER_DATA_ON_FAILED_RESTORE; } - if (sa.getBoolean( - R.styleable.AndroidManifestApplication_allowAudioPlaybackCapture, - owner.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.Q)) { - ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_ALLOW_AUDIO_PLAYBACK_CAPTURE; - } + ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_ALLOW_AUDIO_PLAYBACK_CAPTURE; if (sa.getBoolean( R.styleable.AndroidManifestApplication_requestLegacyExternalStorage, @@ -4675,43 +4633,7 @@ private Activity parseActivity(Package owner, Resources res, } private void setActivityResizeMode(ActivityInfo aInfo, TypedArray sa, Package owner) { - final boolean appExplicitDefault = (owner.applicationInfo.privateFlags - & (PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE - | PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_UNRESIZEABLE)) != 0; - - if (sa.hasValue(R.styleable.AndroidManifestActivity_resizeableActivity) - || appExplicitDefault) { - // Activity or app explicitly set if it is resizeable or not; - final boolean appResizeable = (owner.applicationInfo.privateFlags - & PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE) != 0; - if (sa.getBoolean(R.styleable.AndroidManifestActivity_resizeableActivity, - appResizeable)) { - aInfo.resizeMode = RESIZE_MODE_RESIZEABLE; - } else { - aInfo.resizeMode = RESIZE_MODE_UNRESIZEABLE; - } - return; - } - - if ((owner.applicationInfo.privateFlags - & PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION) != 0) { - // The activity or app didn't explicitly set the resizing option, however we want to - // make it resize due to the sdk version it is targeting. - aInfo.resizeMode = RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION; - return; - } - - // resize preference isn't set and target sdk version doesn't support resizing apps by - // default. For the app to be resizeable if it isn't fixed orientation or immersive. - if (aInfo.isFixedOrientationPortrait()) { - aInfo.resizeMode = RESIZE_MODE_FORCE_RESIZABLE_PORTRAIT_ONLY; - } else if (aInfo.isFixedOrientationLandscape()) { - aInfo.resizeMode = RESIZE_MODE_FORCE_RESIZABLE_LANDSCAPE_ONLY; - } else if (aInfo.isFixedOrientation()) { - aInfo.resizeMode = RESIZE_MODE_FORCE_RESIZABLE_PRESERVE_ORIENTATION; - } else { - aInfo.resizeMode = RESIZE_MODE_FORCE_RESIZEABLE; - } + aInfo.resizeMode = RESIZE_MODE_RESIZEABLE; } /** diff --git a/core/java/android/content/pm/PackagePartitions.java b/core/java/android/content/pm/PackagePartitions.java index ff80e614be58b..da3b68ecf789f 100644 --- a/core/java/android/content/pm/PackagePartitions.java +++ b/core/java/android/content/pm/PackagePartitions.java @@ -131,7 +131,7 @@ private static String getFingerprint() { final String partitionName = SYSTEM_PARTITIONS.get(i).getName(); digestProperties[i] = "ro." + partitionName + ".build.fingerprint"; } - digestProperties[SYSTEM_PARTITIONS.size()] = "ro.build.fingerprint"; // build fingerprint + digestProperties[SYSTEM_PARTITIONS.size()] = "ro.build.version.incremental"; return SystemProperties.digestOf(digestProperties); } diff --git a/core/java/android/content/pm/flags.aconfig b/core/java/android/content/pm/flags.aconfig index 4615370c974d5..fd3b59d306b53 100644 --- a/core/java/android/content/pm/flags.aconfig +++ b/core/java/android/content/pm/flags.aconfig @@ -331,12 +331,3 @@ flag { bug: "381949725" is_fixed_read_only: true } - -flag { - name: "verification_service" - namespace: "package_manager_service" - description: "Feature flag to enable the new verification service." - bug: "360129103" - is_fixed_read_only: true -} - diff --git a/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java b/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java index 153dd9a934901..fc06dd8de3b63 100644 --- a/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java +++ b/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java @@ -186,8 +186,8 @@ public static PublicKey parsePublicKey(final byte[] publicKey) { * Returns {@code true} if both the property name and value are empty or if the given system * property is set to the specified value. Properties can be one or more, and if properties are * more than one, they must be separated by comma, and count of names and values must be equal, - * and also every given system property must be set to the corresponding value. - * In all other cases, returns {@code false} + * and also every given system property must be set to the corresponding value or it's a + * wildcard. In all other cases, returns {@code false} */ public static boolean checkRequiredSystemProperties(@Nullable String rawPropNames, @Nullable String rawPropValues) { @@ -213,9 +213,17 @@ public static boolean checkRequiredSystemProperties(@Nullable String rawPropName return false; } for (int i = 0; i < propNames.length; i++) { - // Check property value: make sure it is both set and equal to expected value final String currValue = SystemProperties.get(propNames[i]); - if (!TextUtils.equals(currValue, propValues[i])) { + // 1. Make sure prop is set. + if (currValue == null) { + return false; + } + // 2. Check next prop if expected value is a wildcard. + if ("*".equals(propValues[i])) { + continue; + } + // 3. Check if prop is equal to expected value. + if (!currValue.equals(propValues[i])) { return false; } } diff --git a/core/java/android/content/pm/verify/developer/DeveloperVerificationSession.java b/core/java/android/content/pm/verify/developer/DeveloperVerificationSession.java deleted file mode 100644 index db7828c6777bd..0000000000000 --- a/core/java/android/content/pm/verify/developer/DeveloperVerificationSession.java +++ /dev/null @@ -1,335 +0,0 @@ -/* - * Copyright (C) 2024 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.content.pm.verify.developer; - -import android.annotation.CurrentTimeMillisLong; -import android.annotation.DurationMillisLong; -import android.annotation.FlaggedApi; -import android.annotation.IntDef; -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.annotation.SystemApi; -import android.content.pm.Flags; -import android.content.pm.PackageInstaller; -import android.content.pm.SharedLibraryInfo; -import android.content.pm.SigningInfo; -import android.net.Uri; -import android.os.Parcel; -import android.os.Parcelable; -import android.os.PersistableBundle; -import android.os.RemoteException; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.util.Collections; -import java.util.List; - -/** - * This class is used by the system to describe the details about a developer verification request - * sent to the verification agent, aka the verifier. It includes the interfaces for the verifier to - * communicate back to the system. - * @hide - */ -@FlaggedApi(Flags.FLAG_VERIFICATION_SERVICE) -@SystemApi -public final class DeveloperVerificationSession implements Parcelable { - /** - * The developer verification cannot be completed because of unknown reasons. - */ - public static final int DEVELOPER_VERIFICATION_INCOMPLETE_UNKNOWN = 0; - /** - * The developer verification cannot be completed because the network is unavailable. - */ - public static final int DEVELOPER_VERIFICATION_INCOMPLETE_NETWORK_UNAVAILABLE = 1; - - /** - * @hide - */ - @IntDef(prefix = {"DEVELOPER_VERIFICATION_INCOMPLETE_"}, value = { - DEVELOPER_VERIFICATION_INCOMPLETE_UNKNOWN, - DEVELOPER_VERIFICATION_INCOMPLETE_NETWORK_UNAVAILABLE, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface DeveloperVerificationIncompleteReason { - } - - private final int mId; - private final int mInstallSessionId; - @NonNull - private final String mPackageName; - @NonNull - private final Uri mStagedPackageUri; - @NonNull - private final SigningInfo mSigningInfo; - @NonNull - private final List mDeclaredLibraries; - @Nullable - private final PersistableBundle mExtensionParams; - @NonNull - private final IDeveloperVerificationSessionInterface mSession; - /** - * The current policy that is active for the developer verification session. It might not be - * the same as the original policy that was initially assigned for this verification session, - * because the active policy can be overridden by {@link #setVerificationPolicy(int)}. - *

To improve the latency, store the original policy value and any changes made to it, - * so that {@link #getVerificationPolicy()} does not need to make a binder call to retrieve the - * currently active policy.

- */ - private volatile @PackageInstaller.DeveloperVerificationPolicy int mVerificationPolicy; - - /** - * Constructor used by the system to describe the details of a developer verification session. - * @hide - */ - public DeveloperVerificationSession(int id, int installSessionId, @NonNull String packageName, - @NonNull Uri stagedPackageUri, @NonNull SigningInfo signingInfo, - @NonNull List declaredLibraries, - @Nullable PersistableBundle extensionParams, - @PackageInstaller.DeveloperVerificationPolicy int defaultPolicy, - @NonNull IDeveloperVerificationSessionInterface session) { - mId = id; - mInstallSessionId = installSessionId; - mPackageName = packageName; - mStagedPackageUri = stagedPackageUri; - mSigningInfo = signingInfo; - mDeclaredLibraries = declaredLibraries; - mExtensionParams = extensionParams; - mVerificationPolicy = defaultPolicy; - mSession = session; - } - - /** - * A unique identifier tied to this specific developer verification session. - */ - public int getId() { - return mId; - } - - /** - * The package name of the app that is to be verified. - */ - public @NonNull String getPackageName() { - return mPackageName; - } - - /** - * The id of the installation session associated with the developer verification. - */ - public int getInstallSessionId() { - return mInstallSessionId; - } - - /** - * The Uri of the path where the package's code files are located. - */ - public @NonNull Uri getStagedPackageUri() { - return mStagedPackageUri; - } - - /** - * Signing info of the package to be verified. - */ - public @NonNull SigningInfo getSigningInfo() { - return mSigningInfo; - } - - /** - * Returns a mapping of any shared libraries declared in the manifest - * to the {@link SharedLibraryInfo.Type} that is declared. - *

This will be an empty map if no shared libraries are declared by the package.

- */ - @NonNull - public List getDeclaredLibraries() { - return Collections.unmodifiableList(mDeclaredLibraries); - } - - /** - * Returns any extension params associated with the developer verification request. - */ - @NonNull - public PersistableBundle getExtensionParams() { - if (mExtensionParams == null) { - return PersistableBundle.EMPTY; - } - return mExtensionParams; - } - - /** - * Get the value of Clock.elapsedRealtime() at which time this developer verification session - * will timeout as incomplete if no other verification response is provided. - * @throws SecurityException if the caller is not the current verifier bound by the system. - * @throws IllegalStateException if this is called after the session has finished, because - * the {@link #reportVerificationComplete} or {@link #reportVerificationIncomplete} have - * been called, or because the session has timed out. - */ - public @CurrentTimeMillisLong long getTimeoutTime() { - try { - return mSession.getTimeoutTime(mId); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** - * Return the current policy that is active for this developer verification session. - *

If the policy for this session has been changed by {@link #setVerificationPolicy}, - * the return value of this method is the current policy that is active for this session. - * Otherwise, the return value is the same as the initial policy that was assigned to the - * session when it was first created.

- */ - public @PackageInstaller.DeveloperVerificationPolicy int getVerificationPolicy() { - return mVerificationPolicy; - } - - /** - * Override the verification policy for this developer verification session. - * @return True if the override was successful, False otherwise. - * @throws SecurityException if the caller is not the current verifier bound by the system. - * @throws IllegalStateException if this is called after the session has finished, because - * the {@link #reportVerificationComplete} or {@link #reportVerificationIncomplete} have - * been called, or because the session has timed out, unless the new policy value is the same - * as the existing one. - */ - public boolean setVerificationPolicy(@PackageInstaller.DeveloperVerificationPolicy int policy) { - if (mVerificationPolicy == policy) { - // No effective policy change - return true; - } - try { - if (mSession.setVerificationPolicy(mId, policy)) { - mVerificationPolicy = policy; - return true; - } else { - return false; - } - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** - * Extend the timeout for this developer verification session by the provided additionalMs to - * fetch relevant information over the network or wait for the network. - *

- * This may be called multiple times. If the request would bypass any max - * duration by the system, the method will return a lower value than the - * requested amount that indicates how much the time was extended. - *

- * @throws SecurityException if the caller is not the current verifier bound by the system. - */ - public @DurationMillisLong long extendTimeRemaining(@DurationMillisLong long additionalMs) { - try { - return mSession.extendTimeRemaining(mId, additionalMs); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** - * Report to the system that the developer verification verification could not be completed - * along with an approximate reason to pass on to the installer. - * @throws SecurityException if the caller is not the current verifier bound by the system. - * @throws IllegalStateException if this is called after the session has finished, because - * this API or {@link #reportVerificationComplete} have already been called once, or because the - * session has timed out. - */ - public void reportVerificationIncomplete(@DeveloperVerificationIncompleteReason int reason) { - try { - mSession.reportVerificationIncomplete(mId, reason); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** - * Report to the system that the developer verification verification has completed and the - * install process may act on that status to either block in the case - * of failure or continue to process the install in the case of success. - * @throws SecurityException if the caller is not the current verifier bound by the system. - * @throws IllegalStateException if this is called after the session has finished, because - * this API or {@link #reportVerificationIncomplete} have already been called once, or because - * the session has timed out. - */ - public void reportVerificationComplete(@NonNull DeveloperVerificationStatus status) { - try { - mSession.reportVerificationComplete(mId, status, /* extensionResponse= */ null); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** - * Same as {@link #reportVerificationComplete(DeveloperVerificationStatus)}, but also provide - * a result to the extension params provided in the request, which will be passed to the - * installer in the installation result. - * @throws SecurityException if the caller is not the current verifier bound by the system. - * @throws IllegalStateException if this is called after the session has finished, because - * this API or {@link #reportVerificationIncomplete} have already been called once, or because - * the session has timed out. - */ - public void reportVerificationComplete(@NonNull DeveloperVerificationStatus status, - @NonNull PersistableBundle extensionResponse) { - try { - mSession.reportVerificationComplete(mId, status, extensionResponse); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - private DeveloperVerificationSession(@NonNull Parcel in) { - mId = in.readInt(); - mInstallSessionId = in.readInt(); - mPackageName = in.readString8(); - mStagedPackageUri = Uri.CREATOR.createFromParcel(in); - mSigningInfo = SigningInfo.CREATOR.createFromParcel(in); - mDeclaredLibraries = in.createTypedArrayList(SharedLibraryInfo.CREATOR); - mExtensionParams = in.readPersistableBundle(getClass().getClassLoader()); - mVerificationPolicy = in.readInt(); - mSession = IDeveloperVerificationSessionInterface.Stub.asInterface(in.readStrongBinder()); - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeInt(mId); - dest.writeInt(mInstallSessionId); - dest.writeString8(mPackageName); - Uri.writeToParcel(dest, mStagedPackageUri); - mSigningInfo.writeToParcel(dest, flags); - dest.writeTypedList(mDeclaredLibraries); - dest.writePersistableBundle(mExtensionParams); - dest.writeInt(mVerificationPolicy); - dest.writeStrongBinder(mSession.asBinder()); - } - - @NonNull - public static final Creator CREATOR = new Creator<>() { - @Override - public DeveloperVerificationSession createFromParcel(@NonNull Parcel in) { - return new DeveloperVerificationSession(in); - } - - @Override - public DeveloperVerificationSession[] newArray(int size) { - return new DeveloperVerificationSession[size]; - } - }; -} diff --git a/core/java/android/content/pm/verify/developer/DeveloperVerificationStatus.java b/core/java/android/content/pm/verify/developer/DeveloperVerificationStatus.java deleted file mode 100644 index d7ba1f2f1859c..0000000000000 --- a/core/java/android/content/pm/verify/developer/DeveloperVerificationStatus.java +++ /dev/null @@ -1,204 +0,0 @@ -/* - * Copyright (C) 2024 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.content.pm.verify.developer; - -import android.annotation.FlaggedApi; -import android.annotation.IntDef; -import android.annotation.NonNull; -import android.annotation.SystemApi; -import android.content.pm.Flags; -import android.os.Parcel; -import android.os.Parcelable; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.util.Objects; - -/** - * This class is used by the developer verifier to describe the status of the verification request, - * whether it's successful or it has failed along with any relevant details. - * @hide - */ -@SystemApi -@FlaggedApi(Flags.FLAG_VERIFICATION_SERVICE) -public final class DeveloperVerificationStatus implements Parcelable { - /** - * The ASL status has not been determined. - *

This happens in situations where the verification - * service is not monitoring ASLs, and means the ASL data in the app is not necessarily bad but - * can't be trusted. - *

- */ - public static final int DEVELOPER_VERIFIER_STATUS_ASL_UNDEFINED = 0; - - /** - * The app's ASL data is considered to be in a good state. - */ - public static final int DEVELOPER_VERIFIER_STATUS_ASL_GOOD = 1; - - /** - * There is something bad in the app's ASL data. - *

- * The user should be warned about this when shown - * the ASL data and/or appropriate decisions made about the use of this data by the platform. - *

- */ - public static final int DEVELOPER_VERIFIER_STATUS_ASL_BAD = 2; - - /** @hide */ - @IntDef(prefix = {"DEVELOPER_VERIFIER_STATUS_ASL_"}, value = { - DEVELOPER_VERIFIER_STATUS_ASL_UNDEFINED, - DEVELOPER_VERIFIER_STATUS_ASL_GOOD, - DEVELOPER_VERIFIER_STATUS_ASL_BAD, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface DeveloperVerifierStatusAsl {} - - private final boolean mIsVerified; - private final boolean mIsLite; - private final @DeveloperVerifierStatusAsl int mAslStatus; - @NonNull - private final String mFailuresMessage; - - private DeveloperVerificationStatus(boolean isVerified, boolean isLite, - @DeveloperVerifierStatusAsl int aslStatus, @NonNull String failuresMessage) { - mIsVerified = isVerified; - mIsLite = isLite; - mAslStatus = aslStatus; - mFailuresMessage = failuresMessage; - } - - /** - * @return whether the status is set to verified or not. - */ - public boolean isVerified() { - return mIsVerified; - } - - /** - * @return true when the only the lite variation of the verification was conducted. - */ - public boolean isLite() { - return mIsLite; - } - - /** - * @return the failure message associated with the failure status. - */ - @NonNull - public String getFailureMessage() { - return mFailuresMessage; - } - - /** - * @return the asl status. - */ - public @DeveloperVerifierStatusAsl int getAslStatus() { - return mAslStatus; - } - - /** - * Builder to construct a {@link DeveloperVerificationStatus} object. - */ - public static final class Builder { - private boolean mIsVerified = false; - private boolean mIsLite = false; - private @DeveloperVerifierStatusAsl int mAslStatus = - DEVELOPER_VERIFIER_STATUS_ASL_UNDEFINED; - private String mFailuresMessage = ""; - - /** - * Set in the status whether the verification has succeeded or failed. - */ - @NonNull - public Builder setVerified(boolean isVerified) { - mIsVerified = isVerified; - return this; - } - - /** - * Set in the status whether the lite variation of the verification was conducted - * instead of the full verification. - */ - @NonNull - public Builder setLite(boolean isLite) { - mIsLite = isLite; - return this; - } - - /** - * Set a developer-facing failure message to include in the verification failure status. - */ - @NonNull - public Builder setFailureMessage(@NonNull String failureMessage) { - Objects.requireNonNull(failureMessage, "failureMessage cannot be null"); - mFailuresMessage = failureMessage; - return this; - } - - /** - * Set the ASL status, as defined in {@link DeveloperVerifierStatusAsl}. - */ - @NonNull - public Builder setAslStatus(@DeveloperVerifierStatusAsl int aslStatus) { - mAslStatus = aslStatus; - return this; - } - - /** - * Build the status object. - */ - @NonNull - public DeveloperVerificationStatus build() { - return new DeveloperVerificationStatus(mIsVerified, mIsLite, mAslStatus, - mFailuresMessage); - } - } - - private DeveloperVerificationStatus(Parcel in) { - mIsVerified = in.readBoolean(); - mIsLite = in.readBoolean(); - mAslStatus = in.readInt(); - mFailuresMessage = in.readString8(); - } - - @Override - public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeBoolean(mIsVerified); - dest.writeBoolean(mIsLite); - dest.writeInt(mAslStatus); - dest.writeString8(mFailuresMessage); - } - - @Override - public int describeContents() { - return 0; - } - - @NonNull - public static final Creator CREATOR = new Creator<>() { - @Override - public DeveloperVerificationStatus createFromParcel(@NonNull Parcel in) { - return new DeveloperVerificationStatus(in); - } - - @Override - public DeveloperVerificationStatus[] newArray(int size) { - return new DeveloperVerificationStatus[size]; - } - }; -} diff --git a/core/java/android/content/pm/verify/developer/DeveloperVerifierService.java b/core/java/android/content/pm/verify/developer/DeveloperVerifierService.java deleted file mode 100644 index 28c7606692ac6..0000000000000 --- a/core/java/android/content/pm/verify/developer/DeveloperVerifierService.java +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright (C) 2024 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.content.pm.verify.developer; - -import android.annotation.FlaggedApi; -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.annotation.SystemApi; -import android.app.Service; -import android.content.Intent; -import android.content.pm.Flags; -import android.content.pm.PackageManager; -import android.os.IBinder; - -/** - * A base service implementation for the developer verifier agent to implement. - *

- * The developer verifier agent app should register the implemented {@link DeveloperVerifierService} - * in the manifest. - * Example: - *
{@code
- *     
- *       
- *         
- *       
- *     
- * }
- *

- * Notice that the developer verifier agent app should also declare - * {@code android:forceQueryable="true"} to make itself visible to the installers. - * @hide - */ -@SystemApi -@FlaggedApi(Flags.FLAG_VERIFICATION_SERVICE) -public abstract class DeveloperVerifierService extends Service { - /** - * Called when a package name is available for a pending verification, - * giving the verifier opportunity to pre-fetch any relevant information - * that may be needed should a verification for the package be required. - */ - public abstract void onPackageNameAvailable(@NonNull String packageName); - - /** - * Called when a package recently provided via {@link #onPackageNameAvailable} - * is no longer expected to be installed. - *

This is a hint that any pre-fetch or - * cache created as a result of the previous call may be be cleared.

- *

This method will never be called after {@link #onVerificationRequired} is called for the - * same package. Once a verification is officially requested by - * {@link #onVerificationRequired}, it cannot be cancelled. - *

- */ - public abstract void onVerificationCancelled(@NonNull String packageName); - - /** - * Called when an application needs to be verified. - *

Details about the - * verification and actions that can be taken on it will be encapsulated in - * the provided {@link DeveloperVerificationSession} parameter.

- */ - public abstract void onVerificationRequired(@NonNull DeveloperVerificationSession session); - - /** - * Called when a verification needs to be retried. - *

This can be encountered - * when a prior verification was marked incomplete and the user has indicated - * that they've resolved the issue, or when a timeout is reached, but the - * the system is attempting to retry. - *

- *

Details about the - * verification and actions that can be taken on it will be encapsulated in - * the provided {@link DeveloperVerificationSession} parameter.

- */ - public abstract void onVerificationRetry(@NonNull DeveloperVerificationSession session); - - /** - * Called in the case that an active verification has failed because of the timeout. - */ - public abstract void onVerificationTimeout(int verificationId); - - /** - * Called when the verifier service is bound to the system. - */ - public @Nullable IBinder onBind(@Nullable Intent intent) { - if (intent == null || !PackageManager.ACTION_VERIFY_DEVELOPER.equals(intent.getAction())) { - return null; - } - return new IDeveloperVerifierService.Stub() { - @Override - public void onPackageNameAvailable(@NonNull String packageName) { - DeveloperVerifierService.this.onPackageNameAvailable(packageName); - } - - @Override - public void onVerificationCancelled(@NonNull String packageName) { - DeveloperVerifierService.this.onVerificationCancelled(packageName); - } - - @Override - public void onVerificationRequired(@NonNull DeveloperVerificationSession session) { - DeveloperVerifierService.this.onVerificationRequired(session); - } - - @Override - public void onVerificationRetry(@NonNull DeveloperVerificationSession session) { - DeveloperVerifierService.this.onVerificationRetry(session); - } - - @Override - public void onVerificationTimeout(int verificationId) { - DeveloperVerifierService.this.onVerificationTimeout(verificationId); - } - }; - } -} diff --git a/core/java/android/content/pm/verify/developer/IDeveloperVerificationSessionInterface.aidl b/core/java/android/content/pm/verify/developer/IDeveloperVerificationSessionInterface.aidl deleted file mode 100644 index d8cc75e474a53..0000000000000 --- a/core/java/android/content/pm/verify/developer/IDeveloperVerificationSessionInterface.aidl +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2024 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.content.pm.verify.developer; - -import android.content.pm.verify.developer.DeveloperVerificationStatus; -import android.os.PersistableBundle; - -/** - * Non-oneway interface that allows the developer verifier to communicate with the system. - * @hide - */ -interface IDeveloperVerificationSessionInterface { - long getTimeoutTime(int verificationId); - long extendTimeRemaining(int verificationId, long additionalMs); - boolean setVerificationPolicy(int verificationId, int policy); - void reportVerificationIncomplete(int verificationId, int reason); - void reportVerificationComplete(int verificationId, in DeveloperVerificationStatus status, in @nullable PersistableBundle extensionResponse); -} \ No newline at end of file diff --git a/core/java/android/content/pm/verify/developer/IDeveloperVerifierService.aidl b/core/java/android/content/pm/verify/developer/IDeveloperVerifierService.aidl deleted file mode 100644 index 1531148662ce4..0000000000000 --- a/core/java/android/content/pm/verify/developer/IDeveloperVerifierService.aidl +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2024 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.content.pm.verify.developer; - -import android.content.pm.verify.developer.DeveloperVerificationSession; - -/** - * Oneway interface that allows the system to communicate to the developer verification service - * provider. - * @hide - */ -oneway interface IDeveloperVerifierService { - void onPackageNameAvailable(in String packageName); - void onVerificationCancelled(in String packageName); - void onVerificationRequired(in DeveloperVerificationSession session); - void onVerificationRetry(in DeveloperVerificationSession session); - void onVerificationTimeout(int verificationId); -} \ No newline at end of file diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java index f2108e0a2eac3..d9695dc1f2e61 100644 --- a/core/java/android/content/res/AssetManager.java +++ b/core/java/android/content/res/AssetManager.java @@ -84,6 +84,7 @@ public final class AssetManager implements AutoCloseable { public static final String FRAMEWORK_APK_PATH = getFrameworkApkPath(); private static final String FRAMEWORK_APK_PATH_DEVICE = "/system/framework/framework-res.apk"; private static final String FRAMEWORK_APK_PATH_RAVENWOOD = "ravenwood-data/framework-res.apk"; + private static final String LINEAGE_APK_PATH = "/system/framework/org.lineageos.platform-res.apk"; private static final Object sSync = new Object(); @@ -291,6 +292,7 @@ public static void createSystemAssetsInZygoteLocked(boolean reinitialize, for (String idmapPath : systemIdmapPaths) { apkAssets.add(ApkAssets.loadOverlayFromPath(idmapPath, ApkAssets.PROPERTY_SYSTEM)); } + apkAssets.add(ApkAssets.loadFromPath(LINEAGE_APK_PATH, ApkAssets.PROPERTY_SYSTEM)); sSystemApkAssetsSet = new ArraySet<>(apkAssets); sSystemApkAssets = apkAssets.toArray(new ApkAssets[0]); @@ -971,8 +973,14 @@ public boolean containsAllocatedTable() { @Nullable CharSequence getPooledStringForCookie(int cookie, int id) { - // Cookies map to ApkAssets starting at 1. - return getApkAssets()[cookie - 1].getStringFromPool(id); + ApkAssets[] apkAssets = getApkAssets(); + + if (cookie > 0 && cookie <= apkAssets.length) { + // map cookies starting at 1. + return apkAssets[cookie - 1].getStringFromPool(id); + } + + return null; } /** diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java index 656ad0001293b..11a4aec87cab3 100644 --- a/core/java/android/content/res/Resources.java +++ b/core/java/android/content/res/Resources.java @@ -213,7 +213,7 @@ public static int selectDefaultTheme(int curTheme, int targetSdkVersion) { com.android.internal.R.style.Theme, com.android.internal.R.style.Theme_Holo, com.android.internal.R.style.Theme_DeviceDefault, - com.android.internal.R.style.Theme_DeviceDefault_Light_DarkActionBar); + com.android.internal.R.style.Theme_DeviceDefault_System); } /** @hide */ @@ -502,11 +502,11 @@ public ConfigurationBoundResourceCache getStateListAnimatorCa if (typeface != null) { return typeface; } + } catch (Exception e) { } finally { releaseTempTypedValue(value); } - throw new NotFoundException("Font resource ID #0x" - + Integer.toHexString(id)); + return Typeface.SANS_SERIF; } @NonNull diff --git a/core/java/android/content/res/StringBlock.java b/core/java/android/content/res/StringBlock.java index 290bc10b64a65..7e556022f37f5 100644 --- a/core/java/android/content/res/StringBlock.java +++ b/core/java/android/content/res/StringBlock.java @@ -109,7 +109,7 @@ public CharSequence get(int idx) { @Nullable public CharSequence getSequence(int idx) { synchronized (this) { - if (mStrings != null) { + if (mStrings != null && idx < mStrings.length) { CharSequence res = mStrings[idx]; if (res != null) { return res; @@ -192,7 +192,7 @@ public CharSequence getSequence(int idx) { res = applyStyles(str, style, mStyleIDs); } if (res != null) { - if (mStrings != null) mStrings[idx] = res; + if (mStrings != null && idx < mStrings.length) mStrings[idx] = res; else mSparseStrings.put(idx, res); } return res; diff --git a/core/java/android/content/res/ThemedResourceCache.java b/core/java/android/content/res/ThemedResourceCache.java index c7fcc1a382446..0a6c511ec2251 100644 --- a/core/java/android/content/res/ThemedResourceCache.java +++ b/core/java/android/content/res/ThemedResourceCache.java @@ -27,6 +27,9 @@ import android.util.LongSparseArray; import java.lang.ref.WeakReference; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; /** * Data structure used for caching data against themes. @@ -37,7 +40,7 @@ abstract class ThemedResourceCache { public static final int UNDEFINED_GENERATION = -1; @UnsupportedAppUsage - private ArrayMap>> mThemedEntries; + private HashMap>> mThemedEntries; private LongSparseArray> mUnthemedEntries; private LongSparseArray> mNullThemedEntries; @@ -176,7 +179,7 @@ private LongSparseArray> getThemedLocked(@Nullable Theme t, boo if (mThemedEntries == null) { if (create) { - mThemedEntries = new ArrayMap<>(1); + mThemedEntries = new HashMap<>(1); } else { return null; } @@ -220,9 +223,12 @@ private LongSparseArray> getUnthemedLocked(boolean create) { */ private boolean pruneLocked(@Config int configChanges) { if (mThemedEntries != null) { - for (int i = mThemedEntries.size() - 1; i >= 0; i--) { - if (pruneEntriesLocked(mThemedEntries.valueAt(i), configChanges)) { - mThemedEntries.removeAt(i); + Iterator iterator = mThemedEntries.keySet().iterator(); + while (iterator.hasNext()) { + ThemeKey key = iterator.next(); + LongSparseArray> value = mThemedEntries.get(key); + if (pruneEntriesLocked(value, configChanges)) { + iterator.remove(); } } } diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java index e9122fc57629f..7bb9c7f618046 100644 --- a/core/java/android/database/sqlite/SQLiteDatabase.java +++ b/core/java/android/database/sqlite/SQLiteDatabase.java @@ -844,6 +844,9 @@ private void beginTransaction(@Nullable SQLiteTransactionListener listener, int boolean readOnly = (mode == SQLiteSession.TRANSACTION_MODE_DEFERRED); getThreadSession().beginTransaction(mode, listener, getThreadDefaultConnectionFlags(readOnly), null); + } catch (SQLiteDatabaseCorruptException ex) { + onCorruption(); + throw ex; } finally { releaseReference(); } @@ -857,6 +860,9 @@ public void endTransaction() { acquireReference(); try { getThreadSession().endTransaction(null); + } catch (SQLiteDatabaseCorruptException ex) { + onCorruption(); + throw ex; } finally { releaseReference(); } @@ -974,6 +980,9 @@ private boolean yieldIfContendedHelper(boolean throwIfUnsafe, long sleepAfterYie acquireReference(); try { return getThreadSession().yieldTransaction(sleepAfterYieldDelay, throwIfUnsafe, null); + } catch (SQLiteDatabaseCorruptException ex) { + onCorruption(); + throw ex; } finally { releaseReference(); } diff --git a/core/java/android/debug/AdbNotifications.java b/core/java/android/debug/AdbNotifications.java index 59497943b7255..7be4253755427 100644 --- a/core/java/android/debug/AdbNotifications.java +++ b/core/java/android/debug/AdbNotifications.java @@ -18,6 +18,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; +import android.app.ActivityOptions; import android.app.Notification; import android.app.PendingIntent; import android.content.Context; @@ -81,8 +82,13 @@ public static Notification createNotification(@NonNull Context context, PendingIntent pIntent = null; if (resolveInfo != null) { intent.setPackage(resolveInfo.activityInfo.packageName); + ActivityOptions activityOptions = ActivityOptions.makeBasic(); + activityOptions.setPendingIntentCreatorBackgroundActivityStartMode( + ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED); + pIntent = PendingIntent.getActivityAsUser(context, 0, intent, - PendingIntent.FLAG_IMMUTABLE, null, UserHandle.CURRENT); + PendingIntent.FLAG_IMMUTABLE, activityOptions.toBundle(), + UserHandle.CURRENT); } diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java index 6ec70045f1f41..a3e2bb6e8a9ab 100644 --- a/core/java/android/hardware/Camera.java +++ b/core/java/android/hardware/Camera.java @@ -50,6 +50,7 @@ import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.SystemProperties; import android.text.TextUtils; import android.util.Log; import android.view.Surface; @@ -63,6 +64,7 @@ import java.io.IOException; import java.lang.ref.WeakReference; import java.util.ArrayList; +import java.util.Arrays; import java.util.LinkedHashMap; import java.util.List; import java.util.Objects; @@ -173,6 +175,10 @@ public class Camera { private static final int CAMERA_MSG_RAW_IMAGE_NOTIFY = 0x200; private static final int CAMERA_MSG_PREVIEW_METADATA = 0x400; private static final int CAMERA_MSG_FOCUS_MOVE = 0x800; + /* ### QC ADD-ONS: START */ + private static final int CAMERA_MSG_STATS_DATA = 0x1000; + private static final int CAMERA_MSG_META_DATA = 0x2000; + /* ### QC ADD-ONS: END */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) private long mNativeContext; // accessed by native methods @@ -204,6 +210,17 @@ public class Camera { private boolean mShutterSoundEnabledFromApp = true; private static final int NO_ERROR = 0; + private static final int EACCESS = -13; + private static final int ENODEV = -19; + private static final int EBUSY = -16; + private static final int EINVAL = -22; + private static final int ENOSYS = -38; + private static final int EUSERS = -87; + private static final int EOPNOTSUPP = -95; + /* ### QC ADD-ONS: START */ + private CameraDataCallback mCameraDataCallback; + private CameraMetaDataCallback mCameraMetaDataCallback; + /* ### QC ADD-ONS: END */ /** * Broadcast Action: A new picture is taken by the camera, and the entry of @@ -266,6 +283,25 @@ public class Camera { */ private static final int CAMERA_FACE_DETECTION_SW = 1; + /** + * @hide + */ + public static boolean shouldExposeAuxCamera() { + /** + * Force to expose only two cameras + * if the package name does not falls in this bucket + */ + String packageName = ActivityThread.currentOpPackageName(); + if (packageName == null) + return true; + List packageList = Arrays.asList( + SystemProperties.get("vendor.camera.aux.packagelist", packageName).split(",")); + List packageExcludelist = Arrays.asList( + SystemProperties.get("vendor.camera.aux.packageexcludelist", "").split(",")); + + return packageList.contains(packageName) && !packageExcludelist.contains(packageName); + } + /** * Returns the number of physical cameras available on this device. * The return value of this method might change dynamically if the device @@ -282,7 +318,12 @@ public class Camera { * cameras or an error was encountered enumerating them. */ public static int getNumberOfCameras() { - return getNumberOfCameras(ActivityThread.currentApplication().getApplicationContext()); + int numberOfCameras = + getNumberOfCameras(ActivityThread.currentApplication().getApplicationContext()); + if (!shouldExposeAuxCamera() && numberOfCameras > 2) { + numberOfCameras = 2; + } + return numberOfCameras; } /** @@ -313,6 +354,9 @@ public static int getNumberOfCameras(@NonNull Context context) { * low-level failure). */ public static void getCameraInfo(int cameraId, CameraInfo cameraInfo) { + if (cameraId >= getNumberOfCameras()) { + throw new RuntimeException("Unknown camera ID"); + } Context context = ActivityThread.currentApplication().getApplicationContext(); final int rotationOverride = CameraManager.getRotationOverride(context); getCameraInfo(cameraId, context, rotationOverride, cameraInfo); @@ -387,6 +431,17 @@ public static class CameraInfo { */ public static final int CAMERA_FACING_FRONT = 1; + /* ### QC ADD-ONS: START TBD*/ + /** @hide + * camera is in ZSL mode. + */ + public static final int CAMERA_SUPPORT_MODE_ZSL = 2; + + /** @hide + * camera is in non-ZSL mode. + */ + public static final int CAMERA_SUPPORT_MODE_NONZSL = 3; + /* ### QC ADD-ONS: END */ /** * The direction that the camera faces. It should be * CAMERA_FACING_BACK or CAMERA_FACING_FRONT. @@ -551,6 +606,10 @@ private int cameraInit(int cameraId, Context context, int rotationOverride) { mPostviewCallback = null; mUsingPreviewAllocation = false; mZoomListener = null; + /* ### QC ADD-ONS: START */ + mCameraDataCallback = null; + mCameraMetaDataCallback = null; + /* ### QC ADD-ONS: END */ Looper looper; if ((looper = Looper.myLooper()) != null) { @@ -590,6 +649,9 @@ private boolean shouldForceSlowJpegMode() { /** used by Camera#open, Camera#open(int) */ Camera(int cameraId, @NonNull Context context, int rotationOverride) { + if (cameraId >= getNumberOfCameras()) { + throw new RuntimeException("Unknown camera ID"); + } Objects.requireNonNull(context); final int err = cameraInit(cameraId, context, rotationOverride); if (checkInitErrors(err)) { @@ -1194,7 +1256,23 @@ public void handleMessage(Message msg) { mAutoFocusMoveCallback.onAutoFocusMoving(msg.arg1 == 0 ? false : true, mCamera); } return; + /* ### QC ADD-ONS: START */ + case CAMERA_MSG_STATS_DATA: + int statsdata[] = new int[257]; + for(int i =0; i<257; i++ ) { + statsdata[i] = byteToInt( (byte[])msg.obj, i*4); + } + if (mCameraDataCallback != null) { + mCameraDataCallback.onCameraData(statsdata, mCamera); + } + return; + case CAMERA_MSG_META_DATA: + if (mCameraMetaDataCallback != null) { + mCameraMetaDataCallback.onCameraMetaData((byte[])msg.obj, mCamera); + } + return; + /* ### QC ADD-ONS: END */ default: Log.e(TAG, "Unknown message type " + msg.what); return; @@ -1705,7 +1783,11 @@ private void updateAppOpsPlayAudio() { } catch (RemoteException e) { Log.e(TAG, "Audio service is unavailable for queries"); } - _enableShutterSound(false); + try { + _enableShutterSound(false); + } catch (Exception e) { + Log.e(TAG, "Couldn't disable shutter sound"); + } } else { enableShutterSound(mShutterSoundEnabledFromApp); } @@ -1935,6 +2017,23 @@ public Face() { * as a set. Either they are all valid, or none of them are. */ public Point mouth = null; + + /** + * {@hide} + */ + public int smileDegree = 0; + /** + * {@hide} + */ + public int smileScore = 0; + /** + * {@hide} + */ + public int blinkDetected = 0; + /** + * {@hide} + */ + public int faceRecognised = 0; } /** @@ -2059,6 +2158,27 @@ public Parameters getParameters() { return p; } + /** @hide + * Returns the current cct value of white balance. + * + * If it's in AWB mode, cct is determined by stats/awb module. + * + * If it's in Manual WB mode, it actually returns cct value + * set by user via {@link #setParameters(Camera.Parameters)}. + */ + public int getWBCurrentCCT() { + Parameters p = new Parameters(); + String s = native_getParameters(); + p.unflatten(s); + + int cct = 0; + if (p.getWBCurrentCCT() != null) { + cct = Integer.parseInt(p.getWBCurrentCCT()); + } + + return cct; + } + /** * Returns an empty {@link Parameters} for testing purpose. * @@ -2072,6 +2192,157 @@ public static Parameters getEmptyParameters() { return camera.new Parameters(); } + /* ### QC ADD-ONS: START */ + private static int byteToInt(byte[] b, int offset) { + int value = 0; + for (int i = 0; i < 4; i++) { + int shift = (4 - 1 - i) * 8; + value += (b[(3-i) + offset] & 0x000000FF) << shift; + } + return value; + } + /** @hide + * Handles the callback for when Camera Data is available. + * data is read from the camera. + */ + public interface CameraDataCallback { + /** + * Callback for when camera data is available. + * + * @param data a int array of the camera data + * @param camera the Camera service object + */ + void onCameraData(int[] data, Camera camera); + }; + + /** @hide + * Set camera histogram mode and registers a callback function to run. + * Only valid after startPreview() has been called. + * + * @param cb the callback to run + */ + public final void setHistogramMode(CameraDataCallback cb) + { + mCameraDataCallback = cb; + native_setHistogramMode(cb!=null); + } + private native final void native_setHistogramMode(boolean mode); + + /** @hide + * Set camera histogram command to send data. + * + */ + public final void sendHistogramData() + { + native_sendHistogramData(); + } + private native final void native_sendHistogramData(); + + /** @hide + * Handles the callback for when Camera Meta Data is available. + * Meta data is read from the camera. + */ + public interface CameraMetaDataCallback { + /** + * Callback for when camera meta data is available. + * + * @param data a byte array of the camera meta data + * @param camera the Camera service object + */ + void onCameraMetaData(byte[] data, Camera camera); + }; + + /** @hide + * Set camera meta data and registers a callback function to run. + * Only valid after startPreview() has been called. + * + * @param cb the callback to run + */ + public final void setMetadataCb(CameraMetaDataCallback cb) + { + mCameraMetaDataCallback = cb; + native_setMetadataCb(cb!=null); + } + private native final void native_setMetadataCb(boolean mode); + + /** @hide + * Set camera face detection command to send meta data. + */ + public final void sendMetaData() + { + native_sendMetaData(); + } + private native final void native_sendMetaData(); + + /** @hide + * Configure longshot mode. Available only in ZSL. + * + * @param enable enable/disable this mode + */ + public final void setLongshot(boolean enable) + { + native_setLongshot(enable); + } + private native final void native_setLongshot(boolean enable); + + /** @hide + * Handles the Touch Co-ordinate. + */ + public class Coordinate { + /** + * Sets the x,y co-ordinates for a touch event + * + * @param x the x co-ordinate (pixels) + * @param y the y co-ordinate (pixels) + */ + public Coordinate(int x, int y) { + xCoordinate = x; + yCoordinate = y; + } + /** + * Compares {@code obj} to this co-ordinate. + * + * @param obj the object to compare this co-ordinate with. + * @return {@code true} if the xCoordinate and yCoordinate of {@code obj} is the + * same as those of this coordinate. {@code false} otherwise. + */ + @Override + public boolean equals(Object obj) { + if (!(obj instanceof Coordinate)) { + return false; + } + Coordinate c = (Coordinate) obj; + return xCoordinate == c.xCoordinate && yCoordinate == c.yCoordinate; + } + + /** x co-ordinate for the touch event*/ + public int xCoordinate; + + /** y co-ordinate for the touch event */ + public int yCoordinate; + }; + + /** @hide + * Returns the current focus position. + * + * If it's in AF mode, it's the lens position after af is done. + * + * If it's in Manual Focus mode, it actually returns the value + * set by user via {@link #setParameters(Camera.Parameters)}. + */ + public int getCurrentFocusPosition() { + Parameters p = new Parameters(); + String s = native_getParameters(); + p.unflatten(s); + + int focus_pos = -1; + if (p.getCurrentFocusPosition() != null) { + focus_pos = Integer.parseInt(p.getCurrentFocusPosition()); + } + return focus_pos; + } + + /* ### QC ADD-ONS: END */ /** * Returns a copied {@link Parameters}; for shim use only. * @@ -2336,6 +2607,10 @@ public class Parameters { public static final String WHITE_BALANCE_CLOUDY_DAYLIGHT = "cloudy-daylight"; public static final String WHITE_BALANCE_TWILIGHT = "twilight"; public static final String WHITE_BALANCE_SHADE = "shade"; + /** @hide + * wb manual cct mode. + */ + public static final String WHITE_BALANCE_MANUAL_CCT = "manual-cct"; // Values for color effect settings. public static final String EFFECT_NONE = "none"; @@ -2383,6 +2658,11 @@ public class Parameters { */ public static final String FLASH_MODE_TORCH = "torch"; + /** @hide + * Scene mode is off. + */ + public static final String SCENE_MODE_ASD = "asd"; + /** * Scene mode is off. */ @@ -2459,6 +2739,14 @@ public class Parameters { * Capture the naturally warm color of scenes lit by candles. */ public static final String SCENE_MODE_CANDLELIGHT = "candlelight"; + /** @hide + * SCENE_MODE_BACKLIGHT + **/ + public static final String SCENE_MODE_BACKLIGHT = "backlight"; + /** @hide + * SCENE_MODE_FLOWERS + **/ + public static final String SCENE_MODE_FLOWERS = "flowers"; /** * Applications are looking for a barcode. Camera driver will be @@ -2501,6 +2789,13 @@ public class Parameters { */ public static final String FOCUS_MODE_FIXED = "fixed"; + /** @hide + * Normal focus mode. Applications should call + * {@link #autoFocus(AutoFocusCallback)} to start the focus in this + * mode. + */ + public static final String FOCUS_MODE_NORMAL = "normal"; + /** * Extended depth of field (EDOF). Focusing is done digitally and * continuously. Applications should not call {@link @@ -2553,6 +2848,11 @@ public class Parameters { */ public static final String FOCUS_MODE_CONTINUOUS_PICTURE = "continuous-picture"; + /** @hide + * manual focus mode + */ + public static final String FOCUS_MODE_MANUAL_POSITION = "manual"; + // Indices for focus distance array. /** * The array index of near focus distance for use with @@ -2589,11 +2889,15 @@ public class Parameters { // Formats for setPreviewFormat and setPictureFormat. private static final String PIXEL_FORMAT_YUV422SP = "yuv422sp"; private static final String PIXEL_FORMAT_YUV420SP = "yuv420sp"; + private static final String PIXEL_FORMAT_YUV420SP_ADRENO = "yuv420sp-adreno"; private static final String PIXEL_FORMAT_YUV422I = "yuv422i-yuyv"; private static final String PIXEL_FORMAT_YUV420P = "yuv420p"; private static final String PIXEL_FORMAT_RGB565 = "rgb565"; private static final String PIXEL_FORMAT_JPEG = "jpeg"; private static final String PIXEL_FORMAT_BAYER_RGGB = "bayer-rggb"; + private static final String PIXEL_FORMAT_RAW = "raw"; + private static final String PIXEL_FORMAT_YV12 = "yv12"; + private static final String PIXEL_FORMAT_NV12 = "nv12"; /** * Order matters: Keys that are {@link #set(String, String) set} later @@ -3413,8 +3717,11 @@ public void setGpsProcessingMethod(String processing_method) { * parameters. */ public void removeGpsData() { + remove(KEY_QC_GPS_LATITUDE_REF); remove(KEY_GPS_LATITUDE); + remove(KEY_QC_GPS_LONGITUDE_REF); remove(KEY_GPS_LONGITUDE); + remove(KEY_QC_GPS_ALTITUDE_REF); remove(KEY_GPS_ALTITUDE); remove(KEY_GPS_TIMESTAMP); remove(KEY_GPS_PROCESSING_METHOD); @@ -3584,6 +3891,7 @@ public String getSceneMode() { * @see #getSceneMode() */ public void setSceneMode(String value) { + if(getSupportedSceneModes() == null) return; set(KEY_SCENE_MODE, value); } @@ -3621,6 +3929,7 @@ public String getFlashMode() { * @see #getFlashMode() */ public void setFlashMode(String value) { + if(getSupportedFlashModes() == null) return; set(KEY_FLASH_MODE, value); } @@ -4305,6 +4614,7 @@ private void splitInt(String str, int[] output) { splitter.setString(str); int index = 0; for (String s : splitter) { + s = s.replaceAll("\\s",""); output[index++] = Integer.parseInt(s); } } @@ -4375,7 +4685,7 @@ private Size strToSize(String str) { // Example string: "(10000,26623),(10000,30000)". Return null if the // passing string is null or the size is 0. private ArrayList splitRange(String str) { - if (str == null || str.charAt(0) != '(' + if (str == null || str.isEmpty() || str.charAt(0) != '(' || str.charAt(str.length() - 1) != ')') { Log.e(TAG, "Invalid range list string=" + str); return null; @@ -4401,7 +4711,7 @@ private ArrayList splitRange(String str) { // the passing string is null or the size is 0 or (0,0,0,0,0). @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) private ArrayList splitArea(String str) { - if (str == null || str.charAt(0) != '(' + if (str == null || str.isEmpty() || str.charAt(0) != '(' || str.charAt(str.length() - 1) != ')') { Log.e(TAG, "Invalid area string=" + str); return null; @@ -4438,5 +4748,1231 @@ private boolean same(String s1, String s2) { if (s1 != null && s1.equals(s2)) return true; return false; } + /* ### QC ADD-ONS: START */ + + /* ### QC ADDED PARAMETER KEYS*/ + private static final String KEY_QC_HFR_SIZE = "hfr-size"; + private static final String KEY_QC_PREVIEW_FRAME_RATE_MODE = "preview-frame-rate-mode"; + private static final String KEY_QC_PREVIEW_FRAME_RATE_AUTO_MODE = "frame-rate-auto"; + private static final String KEY_QC_PREVIEW_FRAME_RATE_FIXED_MODE = "frame-rate-fixed"; + private static final String KEY_QC_GPS_LATITUDE_REF = "gps-latitude-ref"; + private static final String KEY_QC_GPS_LONGITUDE_REF = "gps-longitude-ref"; + private static final String KEY_QC_GPS_ALTITUDE_REF = "gps-altitude-ref"; + private static final String KEY_QC_GPS_STATUS = "gps-status"; + private static final String KEY_QC_EXIF_DATETIME = "exif-datetime"; + private static final String KEY_QC_TOUCH_AF_AEC = "touch-af-aec"; + private static final String KEY_QC_TOUCH_INDEX_AEC = "touch-index-aec"; + private static final String KEY_QC_TOUCH_INDEX_AF = "touch-index-af"; + private static final String KEY_QC_MANUAL_FOCUS_POSITION = "manual-focus-position"; + private static final String KEY_QC_MANUAL_FOCUS_POS_TYPE = "manual-focus-pos-type"; + private static final String KEY_QC_SCENE_DETECT = "scene-detect"; + private static final String KEY_QC_ISO_MODE = "iso"; + private static final String KEY_QC_EXPOSURE_TIME = "exposure-time"; + private static final String KEY_QC_MIN_EXPOSURE_TIME = "min-exposure-time"; + private static final String KEY_QC_MAX_EXPOSURE_TIME = "max-exposure-time"; + private static final String KEY_QC_LENSSHADE = "lensshade"; + private static final String KEY_QC_HISTOGRAM = "histogram"; + private static final String KEY_QC_SKIN_TONE_ENHANCEMENT = "skinToneEnhancement"; + private static final String KEY_QC_AUTO_EXPOSURE = "auto-exposure"; + private static final String KEY_QC_SHARPNESS = "sharpness"; + private static final String KEY_QC_MAX_SHARPNESS = "max-sharpness"; + private static final String KEY_QC_CONTRAST = "contrast"; + private static final String KEY_QC_MAX_CONTRAST = "max-contrast"; + private static final String KEY_QC_SATURATION = "saturation"; + private static final String KEY_QC_MAX_SATURATION = "max-saturation"; + private static final String KEY_QC_DENOISE = "denoise"; + private static final String KEY_QC_CONTINUOUS_AF = "continuous-af"; + private static final String KEY_QC_SELECTABLE_ZONE_AF = "selectable-zone-af"; + private static final String KEY_QC_FACE_DETECTION = "face-detection"; + private static final String KEY_QC_MEMORY_COLOR_ENHANCEMENT = "mce"; + private static final String KEY_QC_REDEYE_REDUCTION = "redeye-reduction"; + private static final String KEY_QC_ZSL = "zsl"; + private static final String KEY_QC_CAMERA_MODE = "camera-mode"; + private static final String KEY_QC_VIDEO_HIGH_FRAME_RATE = "video-hfr"; + private static final String KEY_QC_VIDEO_HDR = "video-hdr"; + private static final String KEY_QC_POWER_MODE = "power-mode"; + private static final String KEY_QC_POWER_MODE_SUPPORTED = "power-mode-supported"; + private static final String KEY_QC_WB_MANUAL_CCT = "wb-manual-cct"; + private static final String KEY_QC_MIN_WB_CCT = "min-wb-cct"; + private static final String KEY_QC_MAX_WB_CCT = "max-wb-cct"; + private static final String KEY_QC_AUTO_HDR_ENABLE = "auto-hdr-enable"; + private static final String KEY_QC_VIDEO_ROTATION = "video-rotation"; + + /** @hide + * KEY_QC_AE_BRACKET_HDR + **/ + public static final String KEY_QC_AE_BRACKET_HDR = "ae-bracket-hdr"; + + /* ### QC ADDED PARAMETER VALUES*/ + + // Values for touch af/aec settings. + /** @hide + * TOUCH_AF_AEC_OFF + **/ + public static final String TOUCH_AF_AEC_OFF = "touch-off"; + /** @hide + * TOUCH_AF_AEC_ON + **/ + public static final String TOUCH_AF_AEC_ON = "touch-on"; + + // Values for auto exposure settings. + /** @hide + * Auto exposure frame-avg + **/ + public static final String AUTO_EXPOSURE_FRAME_AVG = "frame-average"; + /** @hide + * Auto exposure center weighted + **/ + public static final String AUTO_EXPOSURE_CENTER_WEIGHTED = "center-weighted"; + /** @hide + * Auto exposure spot metering + **/ + public static final String AUTO_EXPOSURE_SPOT_METERING = "spot-metering"; + + //Values for ISO settings + /** @hide + * ISO_AUTO + **/ + public static final String ISO_AUTO = "auto"; + /** @hide + * ISO_HJR + **/ + public static final String ISO_HJR = "ISO_HJR"; + /** @hide + * ISO_100 + **/ + public static final String ISO_100 = "ISO100"; + /** @hide + * ISO_200 + **/ + public static final String ISO_200 = "ISO200"; + /** @hide + * ISO_400 + **/ + public static final String ISO_400 = "ISO400"; + /** @hide + * ISO_800 + **/ + public static final String ISO_800 = "ISO800"; + /** @hide + * ISO_1600 + **/ + public static final String ISO_1600 = "ISO1600"; + + /** @hide + * ISO_3200 + **/ + public static final String ISO_3200 = "ISO3200"; + + //Values for Lens Shading + /** @hide + * LENSSHADE_ENABLE + **/ + public static final String LENSSHADE_ENABLE = "enable"; + /** @hide + * LENSSHADE_DISABLE + **/ + public static final String LENSSHADE_DISABLE= "disable"; + + //Values for Histogram + /** @hide + * Histogram enable + **/ + public static final String HISTOGRAM_ENABLE = "enable"; + /** @hide + * Histogram disable + **/ + public static final String HISTOGRAM_DISABLE= "disable"; + + //Values for Skin Tone Enhancement + /** @hide + * SKIN_TONE_ENHANCEMENT_ENABLE + **/ + public static final String SKIN_TONE_ENHANCEMENT_ENABLE = "enable"; + /** @hide + * SKIN_TONE_ENHANCEMENT_DISABLE + **/ + public static final String SKIN_TONE_ENHANCEMENT_DISABLE= "disable"; + + // Values for MCE settings. + /** @hide + * MCE_ENaBLE + **/ + public static final String MCE_ENABLE = "enable"; + /** @hide + * MCE_DISABLE + **/ + public static final String MCE_DISABLE = "disable"; + + // Values for ZSL settings. + /** @hide + * ZSL_ON + **/ + public static final String ZSL_ON = "on"; + /** @hide + * ZSL_OFF + **/ + public static final String ZSL_OFF = "off"; + + // Values for HDR Bracketing settings. + + /** @hide + * AEC bracketing off + **/ + public static final String AE_BRACKET_HDR_OFF = "Off"; + /** @hide + * AEC bracketing hdr + **/ + public static final String AE_BRACKET_HDR = "HDR"; + /** @hide + * AEC bracketing aec-bracket + **/ + public static final String AE_BRACKET = "AE-Bracket"; + + // Values for Power mode. + /** @hide + * LOW_POWER + **/ + public static final String LOW_POWER = "Low_Power"; + /** @hide + * NORMAL_POWER + **/ + public static final String NORMAL_POWER = "Normal_Power"; + + // Values for HFR settings. + /** @hide + * VIDEO_HFR_OFF + **/ + public static final String VIDEO_HFR_OFF = "off"; + /** @hide + * VIDEO_HFR_2X + **/ + public static final String VIDEO_HFR_2X = "60"; + /** @hide + * VIDEO_HFR_3X + **/ + public static final String VIDEO_HFR_3X = "90"; + /** @hide + * VIDEO_HFR_4X + **/ + public static final String VIDEO_HFR_4X = "120"; + + // Values for auto scene detection settings. + /** @hide + * SCENE_DETECT_OFF + **/ + public static final String SCENE_DETECT_OFF = "off"; + /** @hide + * SCENE_DETECT_ON + **/ + public static final String SCENE_DETECT_ON = "on"; + + //Values for Continuous AF + + /** @hide + * CAF off + **/ + public static final String CONTINUOUS_AF_OFF = "caf-off"; + /** @hide + * CAF on + **/ + public static final String CONTINUOUS_AF_ON = "caf-on"; + /** @hide + * Denoise off + **/ + public static final String DENOISE_OFF = "denoise-off"; + /** @hide + * Denoise on + **/ + public static final String DENOISE_ON = "denoise-on"; + + // Values for Redeye Reduction settings. + /** @hide + * REDEYE_REDUCTION_ENABLE + **/ + public static final String REDEYE_REDUCTION_ENABLE = "enable"; + /** @hide + * REDEYE_REDUCTION_DISABLE + **/ + public static final String REDEYE_REDUCTION_DISABLE = "disable"; + + // Values for selectable zone af settings. + /** @hide + * SELECTABLE_ZONE_AF_AUTO + **/ + public static final String SELECTABLE_ZONE_AF_AUTO = "auto"; + /** @hide + * SELECTABLE_ZONE_AF_SPOTMETERING + **/ + public static final String SELECTABLE_ZONE_AF_SPOTMETERING = "spot-metering"; + /** @hide + * SELECTABLE_ZONE_AF_CENTER_WEIGHTED + **/ + public static final String SELECTABLE_ZONE_AF_CENTER_WEIGHTED = "center-weighted"; + /** @hide + * SELECTABLE_ZONE_AF_FRAME_AVERAGE + **/ + public static final String SELECTABLE_ZONE_AF_FRAME_AVERAGE = "frame-average"; + + // Values for Face Detection settings. + /** @hide + * Face Detection off + **/ + public static final String FACE_DETECTION_OFF = "off"; + /** @hide + * Face Detction on + **/ + public static final String FACE_DETECTION_ON = "on"; + + // Values for video rotation settings. + + /** @hide + * VIDEO_ROTATION_0 + **/ + public static final String VIDEO_ROTATION_0 = "0"; + /** @hide + * VIDEO_ROTATION_90 + **/ + public static final String VIDEO_ROTATION_90 = "90"; + /** @hide + * VIDEO_ROTATION_180 + **/ + public static final String VIDEO_ROTATION_180 = "180"; + /** @hide + * VIDEO_ROTATION_270 + **/ + public static final String VIDEO_ROTATION_270 = "270"; + + /* ### QC ADDED PARAMETER APIS*/ + /** @hide + * Gets the supported preview sizes in high frame rate recording mode. + * + * @return a list of Size object. This method will always return a list + * with at least one element. + */ + public List getSupportedHfrSizes() { + String str = get(KEY_QC_HFR_SIZE + SUPPORTED_VALUES_SUFFIX); + return splitSize(str); + } + + /** @hide + * Gets the supported Touch AF/AEC setting. + * + * @return a List of TOUCH_AF_AEC_XXX string constants. null if TOUCH AF/AEC + * setting is not supported. + * + */ + public List getSupportedTouchAfAec() { + String str = get(KEY_QC_TOUCH_AF_AEC + SUPPORTED_VALUES_SUFFIX); + return split(str); + } + + /** + * Gets the supported Touch AF/AEC setting. + * + * @return a List of TOUCH_AF_AEC_XXX string constants. null if TOUCH AF/AEC + * setting is not supported. + * + */ + + /** @hide + * Gets the supported frame rate modes. + * + * @return a List of FRAME_RATE_XXX_MODE string constant. null if this + * setting is not supported. + */ + public List getSupportedPreviewFrameRateModes() { + String str = get(KEY_QC_PREVIEW_FRAME_RATE_MODE + SUPPORTED_VALUES_SUFFIX); + return split(str); + } + + /** @hide + * Gets the supported auto scene detection modes. + * + * @return a List of SCENE_DETECT_XXX string constant. null if scene detection + * setting is not supported. + * + */ + public List getSupportedSceneDetectModes() { + String str = get(KEY_QC_SCENE_DETECT + SUPPORTED_VALUES_SUFFIX); + return split(str); + } + + /** @hide + * Gets the supported ISO values. + * + * @return a List of FLASH_MODE_XXX string constants. null if flash mode + * setting is not supported. + */ + public List getSupportedIsoValues() { + String str = get(KEY_QC_ISO_MODE + SUPPORTED_VALUES_SUFFIX); + return split(str); + } + + /** @hide + * Gets the supported Lensshade modes. + * + * @return a List of LENS_MODE_XXX string constants. null if lens mode + * setting is not supported. + */ + public List getSupportedLensShadeModes() { + String str = get(KEY_QC_LENSSHADE + SUPPORTED_VALUES_SUFFIX); + return split(str); + } + + /** @hide + * Gets the supported Histogram modes. + * + * @return a List of HISTOGRAM_XXX string constants. null if histogram mode + * setting is not supported. + */ + public List getSupportedHistogramModes() { + String str = get(KEY_QC_HISTOGRAM + SUPPORTED_VALUES_SUFFIX); + return split(str); + } + + /** @hide + * Gets the supported Skin Tone Enhancement modes. + * + * @return a List of SKIN_TONE_ENHANCEMENT_XXX string constants. null if skin tone enhancement + * setting is not supported. + */ + public List getSupportedSkinToneEnhancementModes() { + String str = get(KEY_QC_SKIN_TONE_ENHANCEMENT + SUPPORTED_VALUES_SUFFIX); + return split(str); + } + + /** @hide + * Gets the supported auto exposure setting. + * + * @return a List of AUTO_EXPOSURE_XXX string constants. null if auto exposure + * setting is not supported. + */ + public List getSupportedAutoexposure() { + String str = get(KEY_QC_AUTO_EXPOSURE + SUPPORTED_VALUES_SUFFIX); + return split(str); + } + + /** @hide + * Gets the supported MCE modes. + * + * @return a List of MCE_ENABLE/DISABLE string constants. null if MCE mode + * setting is not supported. + */ + public List getSupportedMemColorEnhanceModes() { + String str = get(KEY_QC_MEMORY_COLOR_ENHANCEMENT + SUPPORTED_VALUES_SUFFIX); + return split(str); + } + + /** @hide + * Gets the supported ZSL modes. + * + * @return a List of ZSL_OFF/OFF string constants. null if ZSL mode + * setting is not supported. + */ + public List getSupportedZSLModes() { + String str = get(KEY_QC_ZSL + SUPPORTED_VALUES_SUFFIX); + return split(str); + } + + /** @hide + * Gets the supported Video HDR modes. + * + * @return a List of Video HDR_OFF/OFF string constants. null if + * Video HDR mode setting is not supported. + */ + public List getSupportedVideoHDRModes() { + String str = get(KEY_QC_VIDEO_HDR + SUPPORTED_VALUES_SUFFIX); + return split(str); + } + + /** @hide + * Gets the supported HFR modes. + * + * @return a List of VIDEO_HFR_XXX string constants. null if hfr mode + * setting is not supported. + */ + public List getSupportedVideoHighFrameRateModes() { + String str = get(KEY_QC_VIDEO_HIGH_FRAME_RATE + SUPPORTED_VALUES_SUFFIX); + return split(str); + } + + /** @hide + * Gets the supported Continuous AF modes. + * + * @return a List of CONTINUOUS_AF_XXX string constant. null if continuous AF + * setting is not supported. + * + */ + public List getSupportedContinuousAfModes() { + String str = get(KEY_QC_CONTINUOUS_AF + SUPPORTED_VALUES_SUFFIX); + return split(str); + } + + /** @hide + * Gets the supported DENOISE modes. + * + * @return a List of DENOISE_XXX string constant. null if DENOISE + * setting is not supported. + * + */ + public List getSupportedDenoiseModes() { + String str = get(KEY_QC_DENOISE + SUPPORTED_VALUES_SUFFIX); + return split(str); + } + + /** @hide + * Gets the supported selectable zone af setting. + * + * @return a List of SELECTABLE_ZONE_AF_XXX string constants. null if selectable zone af + * setting is not supported. + */ + public List getSupportedSelectableZoneAf() { + String str = get(KEY_QC_SELECTABLE_ZONE_AF + SUPPORTED_VALUES_SUFFIX); + return split(str); + } + + /** @hide + * Gets the supported face detection modes. + * + * @return a List of FACE_DETECTION_XXX string constant. null if face detection + * setting is not supported. + * + */ + public List getSupportedFaceDetectionModes() { + String str = get(KEY_QC_FACE_DETECTION + SUPPORTED_VALUES_SUFFIX); + return split(str); + } + + /** @hide + * Gets the supported redeye reduction modes. + * + * @return a List of REDEYE_REDUCTION_XXX string constant. null if redeye reduction + * setting is not supported. + * + */ + public List getSupportedRedeyeReductionModes() { + String str = get(KEY_QC_REDEYE_REDUCTION + SUPPORTED_VALUES_SUFFIX); + return split(str); + } + + /** @hide + * Sets GPS altitude reference. This will be stored in JPEG EXIF header. + * @param altRef reference GPS altitude in meters. + */ + public void setGpsAltitudeRef(double altRef) { + set(KEY_QC_GPS_ALTITUDE_REF, Double.toString(altRef)); + } + + /** @hide + * Sets GPS Status. This will be stored in JPEG EXIF header. + * + * @param status GPS status (UTC in seconds since January 1, + * 1970). + */ + public void setGpsStatus(double status) { + set(KEY_QC_GPS_STATUS, Double.toString(status)); + } + + /** @hide + * Sets the touch co-ordinate for Touch AEC. + * + * @param x the x co-ordinate of the touch event + * @param y the y co-ordinate of the touch event + * + */ + public void setTouchIndexAec(int x, int y) { + String v = Integer.toString(x) + "x" + Integer.toString(y); + set(KEY_QC_TOUCH_INDEX_AEC, v); + } + + /** @hide + * Returns the touch co-ordinates of the touch event. + * + * @return a Index object with the x and y co-ordinated + * for the touch event + * + */ + public Coordinate getTouchIndexAec() { + String pair = get(KEY_QC_TOUCH_INDEX_AEC); + return strToCoordinate(pair); + } + + /** @hide + * Sets the touch co-ordinate for Touch AF. + * + * @param x the x co-ordinate of the touch event + * @param y the y co-ordinate of the touch event + * + */ + public void setTouchIndexAf(int x, int y) { + String v = Integer.toString(x) + "x" + Integer.toString(y); + set(KEY_QC_TOUCH_INDEX_AF, v); + } + + /** @hide + * Returns the touch co-ordinates of the touch event. + * + * @return a Index object with the x and y co-ordinated + * for the touch event + * + */ + public Coordinate getTouchIndexAf() { + String pair = get(KEY_QC_TOUCH_INDEX_AF); + return strToCoordinate(pair); + } + /** @hide + * Set Sharpness Level + * + * @param sharpness level + */ + public void setSharpness(int sharpness){ + if((sharpness < 0) || (sharpness > getMaxSharpness()) ) + throw new IllegalArgumentException( + "Invalid Sharpness " + sharpness); + + set(KEY_QC_SHARPNESS, String.valueOf(sharpness)); + } + + /** @hide + * Set Contrast Level + * + * @param contrast level + */ + public void setContrast(int contrast){ + if((contrast < 0 ) || (contrast > getMaxContrast())) + throw new IllegalArgumentException( + "Invalid Contrast " + contrast); + + set(KEY_QC_CONTRAST, String.valueOf(contrast)); + } + + /** @hide + * Set Saturation Level + * + * @param saturation level + */ + public void setSaturation(int saturation){ + if((saturation < 0 ) || (saturation > getMaxSaturation())) + throw new IllegalArgumentException( + "Invalid Saturation " + saturation); + + set(KEY_QC_SATURATION, String.valueOf(saturation)); + } + + /** @hide + * @return true if full size video snapshot is supported. + */ + public boolean isPowerModeSupported() { + String str = get(KEY_QC_POWER_MODE_SUPPORTED); + return TRUE.equals(str); + } + + /** @hide + * Get Sharpness level + * + * @return sharpness level + */ + public int getSharpness(){ + return getInt(KEY_QC_SHARPNESS); + } + + /** @hide + * Get Max Sharpness Level + * + * @return max sharpness level + */ + public int getMaxSharpness(){ + return getInt(KEY_QC_MAX_SHARPNESS); + } + + /** @hide + * Get Contrast level + * + * @return contrast level + */ + public int getContrast(){ + return getInt(KEY_QC_CONTRAST); + } + + /** @hide + * Get Max Contrast Level + * + * @return max contrast level + */ + public int getMaxContrast(){ + return getInt(KEY_QC_MAX_CONTRAST); + } + + /** @hide + * Get Saturation level + * + * @return saturation level + */ + public int getSaturation(){ + return getInt(KEY_QC_SATURATION); + } + + /** @hide + * Get Max Saturation Level + * + * @return max contrast level + */ + public int getMaxSaturation(){ + return getInt(KEY_QC_MAX_SATURATION); + } + + /** @hide + * Sets GPS latitude reference coordinate. This will be stored in JPEG EXIF + * header. + * @param latRef GPS latitude reference coordinate. + */ + public void setGpsLatitudeRef(String latRef) { + set(KEY_QC_GPS_LATITUDE_REF, latRef); + } + + /** @hide + * Sets GPS longitude reference coordinate. This will be stored in JPEG EXIF + * header. + * @param lonRef GPS longitude reference coordinate. + */ + public void setGpsLongitudeRef(String lonRef) { + set(KEY_QC_GPS_LONGITUDE_REF, lonRef); + } + + /** @hide + * Sets system timestamp. This will be stored in JPEG EXIF header. + * + * @param dateTime current timestamp (UTC in seconds since January 1, + * 1970). + */ + public void setExifDateTime(String dateTime) { + set(KEY_QC_EXIF_DATETIME, dateTime); + } + + /** @hide + * Gets the current Touch AF/AEC setting. + * + * @return one of TOUCH_AF_AEC_XXX string constant. null if Touch AF/AEC + * setting is not supported. + * + */ + public String getTouchAfAec() { + return get(KEY_QC_TOUCH_AF_AEC); + } + + /** @hide + * Sets the current TOUCH AF/AEC setting. + * + * @param value TOUCH_AF_AEC_XXX string constants. + * + */ + public void setTouchAfAec(String value) { + set(KEY_QC_TOUCH_AF_AEC, value); + } + + /** @hide + * Gets the current redeye reduction setting. + * + * @return one of REDEYE_REDUCTION_XXX string constant. null if redeye reduction + * setting is not supported. + * + */ + public String getRedeyeReductionMode() { + return get(KEY_QC_REDEYE_REDUCTION); + } + + /** @hide + * Sets the redeye reduction. Other parameters may be changed after changing + * redeye reduction. After setting redeye reduction, + * applications should call getParameters to know if some parameters are + * changed. + * + * @param value REDEYE_REDUCTION_XXX string constants. + * + */ + public void setRedeyeReductionMode(String value) { + set(KEY_QC_REDEYE_REDUCTION, value); + } + + /** @hide + * Gets the frame rate mode setting. + * + * @return one of FRAME_RATE_XXX_MODE string constant. null if this + * setting is not supported. + */ + public String getPreviewFrameRateMode() { + return get(KEY_QC_PREVIEW_FRAME_RATE_MODE); + } + + /** @hide + * Sets the frame rate mode. + * + * @param value FRAME_RATE_XXX_MODE string constants. + */ + public void setPreviewFrameRateMode(String value) { + set(KEY_QC_PREVIEW_FRAME_RATE_MODE, value); + } + + /** @hide + * Gets the current auto scene detection setting. + * + * @return one of SCENE_DETECT_XXX string constant. null if auto scene detection + * setting is not supported. + * + */ + public String getSceneDetectMode() { + return get(KEY_QC_SCENE_DETECT); + } + + /** @hide + * Sets the auto scene detect. Other parameters may be changed after changing + * scene detect. After setting auto scene detection, + * applications should call getParameters to know if some parameters are + * changed. + * + * @param value SCENE_DETECT_XXX string constants. + * + */ + public void setSceneDetectMode(String value) { + set(KEY_QC_SCENE_DETECT, value); + } + + /** @hide + * Gets the current hdr bracketing mode setting. + * + * @return current hdr bracketing mode. + * @see #KEY_AE_BRACKET_OFF + * @see #KEY_AE_BRACKET_HDR + * @see #KEY_AE_BRACKET_BRACKATING + */ + public String getAEBracket() { + return get(KEY_QC_AE_BRACKET_HDR); + } + + /** @hide + * Sets the Power mode. + * + * @param value Power mode. + * @see #getPowerMode() + */ + public void setPowerMode(String value) { + set(KEY_QC_POWER_MODE, value); + } + + /** @hide + * Gets the current power mode setting. + * + * @return current power mode. null if power mode setting is not + * supported. + * @see #POWER_MODE_LOW + * @see #POWER_MODE_NORMAL + */ + public String getPowerMode() { + return get(KEY_QC_POWER_MODE); + } + + /** @hide + * Set HDR-Bracketing Level + * + * @param value HDR-Bracketing + */ + public void setAEBracket(String value){ + set(KEY_QC_AE_BRACKET_HDR, value); + } + + /** @hide + * Gets the current ISO setting. + * + * @return one of ISO_XXX string constant. null if ISO + * setting is not supported. + */ + public String getISOValue() { + return get(KEY_QC_ISO_MODE); + } + + /** @hide + * Sets the ISO. + * + * @param iso ISO_XXX string constant. + */ + public void setISOValue(String iso) { + set(KEY_QC_ISO_MODE, iso); + } + + /** @hide + * Sets the exposure time. + * + * @param value exposure time. + */ + public void setExposureTime(int value) { + set(KEY_QC_EXPOSURE_TIME, Integer.toString(value)); + } + + /** @hide + * Gets the current exposure time. + * + * @return exposure time. + */ + public String getExposureTime() { + return get(KEY_QC_EXPOSURE_TIME); + } + + /** @hide + * Gets the min supported exposure time. + * + * @return min supported exposure time. + */ + public String getMinExposureTime() { + return get(KEY_QC_MIN_EXPOSURE_TIME); + } + + /** @hide + * Gets the max supported exposure time. + * + * @return max supported exposure time. + */ + public String getMaxExposureTime() { + return get(KEY_QC_MAX_EXPOSURE_TIME); + } + + /** @hide + * Gets the current LensShade Mode. + * + * @return LensShade Mode + */ + public String getLensShade() { + return get(KEY_QC_LENSSHADE); + } + + /** @hide + * Sets the current LensShade Mode. + * + * @return LensShade Mode + */ + public void setLensShade(String lensshade) { + set(KEY_QC_LENSSHADE, lensshade); + } + + /** @hide + * Gets the current auto exposure setting. + * + * @return one of AUTO_EXPOSURE_XXX string constant. null if auto exposure + * setting is not supported. + */ + public String getAutoExposure() { + return get(KEY_QC_AUTO_EXPOSURE); + } + + /** @hide + * Sets the current auto exposure setting. + * + * @param value AUTO_EXPOSURE_XXX string constants. + */ + public void setAutoExposure(String value) { + set(KEY_QC_AUTO_EXPOSURE, value); + } + + /** @hide + * Gets the current MCE Mode. + * + * @return MCE value + */ + public String getMemColorEnhance() { + return get(KEY_QC_MEMORY_COLOR_ENHANCEMENT); + } + + /** @hide + * Sets the current MCE Mode. + * + * @return MCE Mode + */ + public void setMemColorEnhance(String mce) { + set(KEY_QC_MEMORY_COLOR_ENHANCEMENT, mce); + } + + /** @hide + * Set white balance manual cct value. + * + * @param cct user CCT setting. + */ + public void setWBManualCCT(int cct) { + set(KEY_QC_WB_MANUAL_CCT, Integer.toString(cct)); + } + + /** @hide + * Gets the WB min supported CCT. + * + * @return min cct value. + */ + public String getWBMinCCT() { + return get(KEY_QC_MIN_WB_CCT); + } + + /** @hide + * Gets the WB max supported CCT. + * + * @return max cct value. + */ + public String getMaxWBCCT() { + return get(KEY_QC_MAX_WB_CCT); + } + + /** @hide + * Gets the current WB CCT. + * + * @return CCT value + */ + public String getWBCurrentCCT() { + return get(KEY_QC_WB_MANUAL_CCT); + } + + /** @hide + * Gets the current ZSL Mode. + * + * @return ZSL mode value + */ + public String getZSLMode() { + return get(KEY_QC_ZSL); + } + + /** @hide + * Sets the current ZSL Mode. ZSL mode is set as a 0th bit in KEY_CAMERA_MODE. + * + * @return null + */ + public void setZSLMode(String zsl) { + set(KEY_QC_ZSL, zsl); + } + + /** @hide + * Sets the current Auto HDR Mode. + * @ auto_hdr auto hdr string for enable/disable + * @return null + */ + public void setAutoHDRMode(String auto_hdr){ + set(KEY_QC_AUTO_HDR_ENABLE,auto_hdr); + } + + /** @hide + * Gets the current Camera Mode Flag. Camera mode includes a + * flag(byte) which indicates different camera modes. + * For now support for ZSL added at bit0 + * + * @return Camera Mode. + */ + public String getCameraMode() { + return get(KEY_QC_CAMERA_MODE); + } + + /** @hide + * Sets the current Camera Mode. + * + * @return null + */ + public void setCameraMode(int cameraMode) { + set(KEY_QC_CAMERA_MODE, cameraMode); + } + + private static final int MANUAL_FOCUS_POS_TYPE_INDEX = 0; + private static final int MANUAL_FOCUS_POS_TYPE_DAC = 1; + /** @hide + * Set focus position. + * + * @param pos user setting of focus position. + */ + public void setFocusPosition(int type, int pos) { + set(KEY_QC_MANUAL_FOCUS_POS_TYPE, Integer.toString(type)); + set(KEY_QC_MANUAL_FOCUS_POSITION, Integer.toString(pos)); + } + + /** @hide + * Gets the current focus position. + * + * @return current focus position + */ + public String getCurrentFocusPosition() { + return get(KEY_QC_MANUAL_FOCUS_POSITION); + } + + + /** @hide + * Gets the current HFR Mode. + * + * @return VIDEO_HFR_XXX string constants + */ + public String getVideoHighFrameRate() { + return get(KEY_QC_VIDEO_HIGH_FRAME_RATE); + } + + /** @hide + * Sets the current HFR Mode. + * + * @param hfr VIDEO_HFR_XXX string constants + */ + public void setVideoHighFrameRate(String hfr) { + set(KEY_QC_VIDEO_HIGH_FRAME_RATE, hfr); + } + + /** @hide + * Gets the current Video HDR Mode. + * + * @return Video HDR mode value + */ + public String getVideoHDRMode() { + return get(KEY_QC_VIDEO_HDR); + } + + /** @hide + * Sets the current Video HDR Mode. + * + * @return null + */ + public void setVideoHDRMode(String videohdr) { + set(KEY_QC_VIDEO_HDR, videohdr); + } + + /** @hide + * Gets the current DENOISE setting. + * + * @return one of DENOISE_XXX string constant. null if Denoise + * setting is not supported. + * + */ + public String getDenoise() { + return get(KEY_QC_DENOISE); + } + + /** @hide + * Gets the current Continuous AF setting. + * + * @return one of CONTINUOUS_AF_XXX string constant. null if continuous AF + * setting is not supported. + * + */ + public String getContinuousAf() { + return get(KEY_QC_CONTINUOUS_AF); + } + + /** @hide + * Sets the current Denoise mode. + * @param value DENOISE_XXX string constants. + * + */ + + public void setDenoise(String value) { + set(KEY_QC_DENOISE, value); + } + + /** @hide + * Sets the current Continuous AF mode. + * @param value CONTINUOUS_AF_XXX string constants. + * + */ + public void setContinuousAf(String value) { + set(KEY_QC_CONTINUOUS_AF, value); + } + + /** @hide + * Gets the current selectable zone af setting. + * + * @return one of SELECTABLE_ZONE_AF_XXX string constant. null if selectable zone af + * setting is not supported. + */ + public String getSelectableZoneAf() { + return get(KEY_QC_SELECTABLE_ZONE_AF); + } + + /** @hide + * Sets the current selectable zone af setting. + * + * @param value SELECTABLE_ZONE_AF_XXX string constants. + */ + public void setSelectableZoneAf(String value) { + set(KEY_QC_SELECTABLE_ZONE_AF, value); + } + + /** @hide + * Gets the current face detection setting. + * + * @return one of FACE_DETECTION_XXX string constant. null if face detection + * setting is not supported. + * + */ + public String getFaceDetectionMode() { + return get(KEY_QC_FACE_DETECTION); + } + + /** @hide + * Sets the auto scene detect. Other settings like Touch AF/AEC might be + * changed after setting face detection. + * + * @param value FACE_DETECTION_XXX string constants. + * + */ + public void setFaceDetectionMode(String value) { + set(KEY_QC_FACE_DETECTION, value); + } + + /** @hide + * Gets the current video rotation setting. + * + * @return one of VIDEO_QC_ROTATION_XXX string constant. null if video rotation + * setting is not supported. + */ + public String getVideoRotation() { + return get(KEY_QC_VIDEO_ROTATION); + } + + /** @hide + * Sets the current video rotation setting. + * + * @param value VIDEO_QC_ROTATION_XXX string constants. + */ + public void setVideoRotation(String value) { + set(KEY_QC_VIDEO_ROTATION, value); + } + /** @hide + * Gets the supported video rotation modes. + * + * @return a List of VIDEO_QC_ROTATION_XXX string constant. null if this + * setting is not supported. + */ + public List getSupportedVideoRotationValues() { + String str = get(KEY_QC_VIDEO_ROTATION + SUPPORTED_VALUES_SUFFIX); + return split(str); + } + + // Splits a comma delimited string to an ArrayList of Coordinate. + // Return null if the passing string is null or the Coordinate is 0. + private ArrayList splitCoordinate(String str) { + if (str == null) return null; + TextUtils.StringSplitter splitter = new TextUtils.SimpleStringSplitter(','); + splitter.setString(str); + ArrayList coordinateList = new ArrayList(); + for (String s : splitter) { + Coordinate coordinate = strToCoordinate(s); + if (coordinate != null) coordinateList.add(coordinate); + } + if (coordinateList.size() == 0) return null; + return coordinateList; + } + + // Parses a string (ex: "500x500") to Coordinate object. + // Return null if the passing string is null. + private Coordinate strToCoordinate(String str) { + if (str == null) return null; + + int pos = str.indexOf('x'); + if (pos != -1) { + String x = str.substring(0, pos); + String y = str.substring(pos + 1); + return new Coordinate(Integer.parseInt(x), + Integer.parseInt(y)); + } + Log.e(TAG, "Invalid Coordinate parameter string=" + str); + return null; + } + /* ### QC ADD-ONS: END */ }; } diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java index 57fb3a51328b8..8acfa018afbca 100644 --- a/core/java/android/hardware/SystemSensorManager.java +++ b/core/java/android/hardware/SystemSensorManager.java @@ -27,15 +27,20 @@ import android.compat.annotation.EnabledAfter; import android.compat.annotation.UnsupportedAppUsage; import android.content.BroadcastReceiver; +import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ApplicationInfo; +import android.database.ContentObserver; +import android.net.Uri; import android.os.Build; import android.os.Handler; import android.os.Looper; import android.os.MemoryFile; import android.os.MessageQueue; +import android.provider.Settings; +import android.text.TextUtils; import android.util.Log; import android.util.SparseArray; import android.util.SparseBooleanArray; @@ -54,6 +59,8 @@ import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; /** * Sensor manager implementation that communicates with the built-in @@ -143,6 +150,10 @@ private static native int nativeSetOperationParameter( private Optional mHasHighSamplingRateSensorsPermission = Optional.empty(); + private String mBlockedPackageList; + private final Set mBlockedApps = + Collections.newSetFromMap(new ConcurrentHashMap<>()); + /** {@hide} */ public SystemSensorManager(Context context, Looper mainLooper) { synchronized (sLock) { @@ -166,6 +177,12 @@ public SystemSensorManager(Context context, Looper mainLooper) { mFullSensorsList.add(sensor); mHandleToSensor.put(sensor.getHandle(), sensor); } + + parsePackageList(); + + SettingsObserver observer = new SettingsObserver( + new Handler(mMainLooper)); + observer.observe(); } /** @hide */ @@ -238,6 +255,76 @@ protected List getFullDynamicSensorList() { return mFullDynamicSensorsList; } + private void parsePackageList() { + String blockedApp = Settings.Global.getString(mContext.getContentResolver(), + Settings.Global.SENSOR_BLOCKED_APP); + if (blockedApp == null) { + blockedApp = TextUtils.join("|", mContext.getResources().getStringArray( + com.android.internal.R.array.config_blockPackagesSensorDrain)); + } + splitAndAddToArrayList(blockedApp, "\\|"); + } + + private void savePackageList(ArrayList arrayList) { + String setting = Settings.Global.SENSOR_BLOCKED_APP; + + List settings = new ArrayList(); + for (String app : arrayList) { + settings.add(app.toString()); + } + final String value = TextUtils.join("|", settings); + if (TextUtils.equals(setting, Settings.Global.SENSOR_BLOCKED_APP)) { + mBlockedPackageList = value; + } + Settings.Global.putString(mContext.getContentResolver(), + setting, value); + } + + private void addBlockedApp(String packageName) { + if (mBlockedApps.add(packageName)) { + savePackageList(new ArrayList<>(mBlockedApps)); + } + } + + private boolean isBlockedApp(String packageName) { + return mBlockedApps.contains(packageName); + } + + public void notePackageUninstalled(String pkgName) { + if (mBlockedApps.remove(pkgName)) { + savePackageList(new ArrayList<>(mBlockedApps)); + } + } + + private void splitAndAddToArrayList(String baseString, String separator) { + mBlockedApps.clear(); + if (baseString != null) { + for (String s : TextUtils.split(baseString, separator)) { + final String v = s.trim(); + if (!v.isEmpty()) mBlockedApps.add(v); + } + } + } + + class SettingsObserver extends ContentObserver { + SettingsObserver(Handler handler) { + super(handler); + } + + void observe() { + ContentResolver resolver = mContext.getContentResolver(); + resolver.registerContentObserver(Settings.Global.getUriFor( + Settings.Global.SENSOR_BLOCKED_APP), false, this); + resolver.registerContentObserver(Settings.Global.getUriFor( + Settings.Global.SENSOR_BLOCK), false, this); + } + + @Override + public void onChange(boolean selfChange, Uri uri) { + parsePackageList(); + } + } + /** @hide */ @Override protected boolean registerListenerImpl(SensorEventListener listener, Sensor sensor, @@ -255,6 +342,21 @@ protected boolean registerListenerImpl(SensorEventListener listener, Sensor sens Log.e(TAG, "maxBatchReportLatencyUs and delayUs should be non-negative"); return false; } + + if (Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.SENSOR_BLOCK, 0) == 1) { + int sensortype = sensor.getType(); + if (sensortype == Sensor.TYPE_SIGNIFICANT_MOTION || + sensortype == Sensor.TYPE_ACCELEROMETER || + sensortype == Sensor.TYPE_LINEAR_ACCELERATION) { + String pkgName = mContext.getPackageName(); + if (isBlockedApp(pkgName)) { + Log.w(TAG, "Preventing " + pkgName + " from using " + sensor.getStringType()); + return false; + } + } + } + if (mSensorListeners.size() >= MAX_LISTENER_COUNT) { Log.e(TAG, "Too many sensor listeners! Dump:"); Map listenerCounts = new HashMap<>(); @@ -336,6 +438,20 @@ protected boolean requestTriggerSensorImpl(TriggerEventListener listener, Sensor if (sensor.getReportingMode() != Sensor.REPORTING_MODE_ONE_SHOT) return false; + if (Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.SENSOR_BLOCK, 0) == 1) { + final int sensortype = sensor.getType(); + if (sensortype == Sensor.TYPE_SIGNIFICANT_MOTION || + sensortype == Sensor.TYPE_ACCELEROMETER || + sensortype == Sensor.TYPE_LINEAR_ACCELERATION) { + final String pkgName = mContext.getPackageName(); + if (isBlockedApp(pkgName)) { + Log.w(TAG, "Preventing " + pkgName + " from using " + sensor.getStringType()); + return false; + } + } + } + if (mTriggerListeners.size() >= MAX_LISTENER_COUNT) { throw new IllegalStateException("request failed, " + "the trigger listeners size has exceeded the maximum limit " diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java index affc5004403c0..08c39961aa3ba 100644 --- a/core/java/android/hardware/camera2/CameraDevice.java +++ b/core/java/android/hardware/camera2/CameraDevice.java @@ -1404,6 +1404,10 @@ public abstract CaptureRequest.Builder createReprocessCaptureRequest( @Override public abstract void close(); + /** @hide */ + public abstract void setVendorStreamConfigMode(int index) + throws CameraAccessException; + /** * Checks whether a particular {@link SessionConfiguration} is supported by the camera device. * diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java index ccbcf1702d770..e99c3a3555c67 100644 --- a/core/java/android/hardware/camera2/CameraManager.java +++ b/core/java/android/hardware/camera2/CameraManager.java @@ -44,6 +44,7 @@ import android.content.Context; import android.content.pm.PackageManager; import android.graphics.Point; +import android.hardware.Camera; import android.hardware.CameraExtensionSessionStats; import android.hardware.CameraStatus; import android.hardware.ICameraService; @@ -2538,7 +2539,12 @@ private String[] extractCameraIdListLocked(int deviceId, int devicePolicy) { Thread.currentThread().getId(), mDeviceStatus.size())); try { List cameraIds = new ArrayList<>(); - for (int i = 0; i < mDeviceStatus.size(); i++) { + boolean exposeAuxCamera = Camera.shouldExposeAuxCamera(); + int size = exposeAuxCamera ? mDeviceStatus.size() : 2; + if (mDeviceStatus.size() < size) { + size = mDeviceStatus.size(); + } + for (int i = 0; i < size; i++) { int status = mDeviceStatus.valueAt(i); DeviceCameraInfo info = mDeviceStatus.keyAt(i); if (status == ICameraServiceListener.STATUS_NOT_PRESENT @@ -2588,6 +2594,12 @@ private Set> extractConcurrentCameraIdListLocked(int deviceId, } private static void sortCameraIds(String[] cameraIds) { + // Check if the cameraIds array is null to avoid NullPointerException + if (cameraIds == null) { + Log.e("CameraManagerGlobal", "Camera ID array is null"); + return; + } + // The sort logic must match the logic in // libcameraservice/common/CameraProviderManager.cpp::getAPI1CompatibleCameraDeviceIds Arrays.sort(cameraIds, new Comparator() { @@ -2862,6 +2874,14 @@ public void setTorchMode( throw new IllegalArgumentException("cameraId was null"); } + /* Force to expose only two cameras + * if the package name does not falls in this bucket + */ + boolean exposeAuxCamera = Camera.shouldExposeAuxCamera(); + if (exposeAuxCamera == false && (Integer.parseInt(cameraId) >= 2)) { + throw new IllegalArgumentException("invalid cameraId"); + } + ICameraService cameraService = getCameraService(); if (cameraService == null) { throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED, @@ -3120,6 +3140,11 @@ private void updateCallbackLocked(AvailabilityCallback callback, Executor execut } private void onStatusChangedLocked(int status, DeviceCameraInfo info) { + if (!Camera.shouldExposeAuxCamera() && Integer.parseInt(info.mCameraId) >= 2) { + Log.w(TAG, String.format("Ignoring status update of camera %d", info.mDeviceId)); + return; + } + if (DEBUG) { Log.v(TAG, String.format("Camera id %s has status changed to 0x%x for device %d", @@ -3280,6 +3305,15 @@ private void onTorchStatusChangedLocked(int status, DeviceCameraInfo info) { info.mCameraId, status, info.mDeviceId)); } + /* Force to ignore the aux or composite camera torch status update + * if the package name does not falls in this bucket + */ + boolean exposeAuxCamera = Camera.shouldExposeAuxCamera(); + if (exposeAuxCamera == false && Integer.parseInt(info.mCameraId) >= 2) { + Log.w(TAG, "ignore the torch status update of camera: " + info.mCameraId); + return; + } + if (!validTorchStatus(status)) { Log.e(TAG, String.format( "Ignoring invalid camera %s torch status 0x%x for device %d", diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java index 5d3a4db27b645..b5edeb22b91a6 100644 --- a/core/java/android/hardware/camera2/CaptureRequest.java +++ b/core/java/android/hardware/camera2/CaptureRequest.java @@ -235,7 +235,7 @@ public CameraMetadataNative.Key getNativeKey() { private static final ArraySet mEmptySurfaceSet = new ArraySet(); private String mLogicalCameraId; - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage private CameraMetadataNative mLogicalCameraSettings; private final HashMap mPhysicalCameraSettings = new HashMap(); diff --git a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java index 7e0456b22be8d..f4fd2f0839fae 100644 --- a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java @@ -15,6 +15,7 @@ */ package android.hardware.camera2.impl; +import android.app.ActivityThread; import android.hardware.camera2.CameraAccessException; import android.hardware.camera2.CameraCaptureSession; import android.hardware.camera2.CameraDevice; @@ -28,6 +29,7 @@ import android.os.Binder; import android.os.Handler; import android.os.SystemClock; +import android.os.SystemProperties; import android.util.Log; import android.view.Surface; @@ -131,6 +133,18 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession Log.e(TAG, mIdString + "Failed to create capture session; configuration failed"); mConfigureSuccess = false; } + + setSkipUnconfigure(); + } + + private void setSkipUnconfigure() { + String packageName = ActivityThread.currentOpPackageName(); + List packageList = Arrays.asList(SystemProperties.get( + "vendor.camera.skip_unconfigure.packagelist", packageName).split(",")); + + if (packageList.contains(packageName)) { + mSkipUnconfigure = true; + } } @Override @@ -222,7 +236,8 @@ private void checkCaptureRequest(CaptureRequest request) { } else if (request.isReprocess() && !isReprocessable()) { throw new IllegalArgumentException("this capture session cannot handle reprocess " + "requests"); - } else if (request.isReprocess() && request.getReprocessableSessionId() != mId) { + } else if (!mDeviceImpl.isPrivilegedApp() && + request.isReprocess() && request.getReprocessableSessionId() != mId) { throw new IllegalArgumentException("capture request was created for another session"); } } diff --git a/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java index 87553d8c42ab2..bbac7e3796153 100644 --- a/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java @@ -26,11 +26,13 @@ import android.hardware.camera2.CaptureRequest; import android.hardware.camera2.params.OutputConfiguration; import android.hardware.camera2.params.StreamConfigurationMap; +import android.hardware.camera2.params.HighSpeedVideoConfiguration; import android.hardware.camera2.utils.SurfaceUtils; import android.os.Handler; import android.os.ConditionVariable; import android.util.Range; import android.util.Log; +import android.util.Size; import android.view.Surface; import java.util.ArrayList; @@ -118,7 +120,7 @@ public List createHighSpeedRequestList(CaptureRequest request) } Log.v(TAG, "previewFps: " + previewFps); - int requestListSize = fpsRange.getUpper() / previewFps; + int requestListSize = getHighSpeedRequestListSize(fpsRange, outputSurfaces); // If it's a preview, keep requestList size fixed = 1. if (fpsRange.getUpper() > fpsRange.getLower()) { requestListSize = 1; @@ -203,6 +205,34 @@ private boolean isConstrainedHighSpeedRequestList(List requestLi return true; } + private int getHighSpeedRequestListSize(Range fpsRange, Collection surfaces) { + int requestListSize = 0; + + for (Surface surface : surfaces) { + + if (SurfaceUtils.isSurfaceForHwVideoEncoder(surface)) { + Size surfaceSize = SurfaceUtils.getSurfaceSize(surface); + HighSpeedVideoConfiguration[] highSpeedVideoConfigurations = + mCharacteristics.get(CameraCharacteristics.CONTROL_AVAILABLE_HIGH_SPEED_VIDEO_CONFIGURATIONS); + + // Get the batchsize for matching FPS & video size + for (HighSpeedVideoConfiguration config : highSpeedVideoConfigurations) { + if (config.getSize().equals(surfaceSize) && config.getFpsRange().equals(fpsRange)) { + requestListSize = config.getBatchSizeMax(); + break; + } + } + break; + } + } + + if (requestListSize == 0) { + // If cant' find the matching batch size, limit the preview to 30fps. + requestListSize = fpsRange.getUpper() / 30; + } + return requestListSize; + } + @Override public CameraDevice getDevice() { return mSessionImpl.getDevice(); diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java index 8df64b636ad68..9de7bcad41fa7 100644 --- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java @@ -21,6 +21,7 @@ import android.annotation.FlaggedApi; import android.annotation.NonNull; import android.annotation.Nullable; +import android.app.ActivityThread; import android.app.compat.CompatChanges; import android.compat.annotation.ChangeId; import android.compat.annotation.EnabledSince; @@ -65,6 +66,8 @@ import android.os.RemoteException; import android.os.ServiceSpecificException; import android.os.SystemClock; +import android.os.SystemProperties; +import android.text.TextUtils; import android.util.Log; import android.util.Range; import android.util.Size; @@ -111,6 +114,8 @@ public Thread newThread(Runnable r) { private static final int REQUEST_ID_NONE = -1; + private int customOpMode = 0; + /** * Starting {@link Build.VERSION_CODES#VANILLA_ICE_CREAM}, * {@link #isSessionConfigurationSupported} also checks for compatibility of session parameters @@ -194,6 +199,7 @@ public Thread newThread(Runnable r) { private int mNextSessionId = 0; private final int mAppTargetSdkVersion; + private boolean mIsPrivilegedApp = false; private ExecutorService mOfflineSwitchService; private CameraOfflineSessionImpl mOfflineSessionImpl; @@ -445,6 +451,7 @@ public CameraDeviceImpl(String cameraId, StateCallback callback, Executor execut } else { mTotalPartialCount = partialCount; } + mIsPrivilegedApp = checkPrivilegedAppList(); } /** @@ -576,6 +583,10 @@ public void run() { } } + public void setVendorStreamConfigMode(int fpsrange) { + customOpMode = fpsrange; + } + @Override public String getId() { return mCameraId; @@ -632,7 +643,11 @@ public boolean configureStreamsChecked(InputConfiguration inputConfig, if (!checkSurfaceSizesCompatible(outputs)) { return false; } - checkInputConfiguration(inputConfig); + try { + checkInputConfiguration(inputConfig); + } catch (IllegalArgumentException e) { + Log.w(TAG, "Check input configuration failed due to: " + e.getMessage()); + } boolean success = false; @@ -701,6 +716,7 @@ public boolean configureStreamsChecked(InputConfiguration inputConfig, mConfiguredOutputs.put(streamId, outConfig); } } + operatingMode = (operatingMode | (customOpMode << 16)); int offlineStreamIds[]; if (sessionParams != null) { @@ -1873,6 +1889,27 @@ private boolean checkInputConfigurationWithStreamConfigurations( return false; } + private boolean checkPrivilegedAppList() { + String packageName = ActivityThread.currentOpPackageName(); + String packageList = SystemProperties.get("persist.vendor.camera.privapp.list"); + + if (packageList.length() > 0) { + TextUtils.StringSplitter splitter = new TextUtils.SimpleStringSplitter(','); + splitter.setString(packageList); + for (String str : splitter) { + if (packageName.equals(str)) { + return true; + } + } + } + + return false; + } + + public boolean isPrivilegedApp() { + return mIsPrivilegedApp; + } + private boolean checkSurfaceSizesCompatible(List outputConfigs) { for (OutputConfiguration outputConfig : outputConfigs) { Size configuredSize = outputConfig.getConfiguredSize(); @@ -1931,6 +1968,15 @@ private void checkInputConfiguration(InputConfiguration inputConfig) { inputConfig.getWidth() + "x" + inputConfig.getHeight() + " is not valid"); } } else { + /* + * don't check input format and size, + * if the package name is in the white list + */ + if (isPrivilegedApp()) { + Log.w(TAG, "ignore input format/size check for white listed app"); + return; + } + if (!checkInputConfigurationWithStreamConfigurations(inputConfig, /*maxRes*/false) && !checkInputConfigurationWithStreamConfigurations(inputConfig, /*maxRes*/true)) { throw new IllegalArgumentException("Input config with format " + diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java index d7b6f116e4525..2570afaf1d6d4 100644 --- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java +++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java @@ -605,36 +605,40 @@ private T getBase(CaptureRequest.Key key) { } private T getBase(Key key) { - int tag, nativeType; - byte[] values = null; - synchronized (this) { - if (key.hasTag()) { - tag = key.getTag(); - } else { - tag = nativeGetTagFromKeyLocal(mMetadataPtr, key.getName()); - key.cacheTag(tag); - } - values = readValues(tag); - if (values == null) { - // If the key returns null, use the fallback key if exists. - // This is to support old key names for the newly published keys. - if (key.mFallbackName == null) { - return null; + try { + int tag, nativeType; + byte[] values = null; + synchronized (this) { + if (key.hasTag()) { + tag = key.getTag(); + } else { + tag = nativeGetTagFromKeyLocal(mMetadataPtr, key.getName()); + key.cacheTag(tag); } - tag = nativeGetTagFromKeyLocal(mMetadataPtr, key.mFallbackName); values = readValues(tag); if (values == null) { - return null; + // If the key returns null, use the fallback key if exists. + // This is to support old key names for the newly published keys. + if (key.mFallbackName == null) { + return null; + } + tag = nativeGetTagFromKeyLocal(mMetadataPtr, key.mFallbackName); + values = readValues(tag); + if (values == null) { + return null; + } } - } - nativeType = nativeGetTypeFromTagLocal(mMetadataPtr, tag); + nativeType = nativeGetTypeFromTagLocal(mMetadataPtr, tag); + } + // This block of code doesn't need to be synchronized since we aren't writing or reading + // from the metadata buffer for this instance of CameraMetadataNative. + Marshaler marshaler = getMarshalerForKey(key, nativeType); + ByteBuffer buffer = ByteBuffer.wrap(values).order(ByteOrder.nativeOrder()); + return marshaler.unmarshal(buffer); + } catch (Exception e) { + return null; } - // This block of code doesn't need to be synchronized since we aren't writing or reading - // from the metadata buffer for this instance of CameraMetadataNative. - Marshaler marshaler = getMarshalerForKey(key, nativeType); - ByteBuffer buffer = ByteBuffer.wrap(values).order(ByteOrder.nativeOrder()); - return marshaler.unmarshal(buffer); } // Use Command pattern here to avoid lots of expensive if/equals checks in get for overridden @@ -1993,30 +1997,34 @@ private void setBase(CaptureRequest.Key key, T value) { // we expect the metadata's properties such as vendor id etc to // stay the same and as a result the whole method should be synchronized for safety. private synchronized void setBase(Key key, T value) { - int tag, nativeType; - if (key.hasTag()) { - tag = key.getTag(); - } else { - tag = nativeGetTagFromKeyLocal(mMetadataPtr, key.getName()); - key.cacheTag(tag); - } - if (value == null) { - // Erase the entry - writeValues(tag, /*src*/null); - return; - } // else update the entry to a new value + try { + int tag, nativeType; + if (key.hasTag()) { + tag = key.getTag(); + } else { + tag = nativeGetTagFromKeyLocal(mMetadataPtr, key.getName()); + key.cacheTag(tag); + } + if (value == null) { + // Erase the entry + writeValues(tag, /*src*/null); + return; + } // else update the entry to a new value - nativeType = nativeGetTypeFromTagLocal(mMetadataPtr, tag); - Marshaler marshaler = getMarshalerForKey(key, nativeType); - int size = marshaler.calculateMarshalSize(value); + nativeType = nativeGetTypeFromTagLocal(mMetadataPtr, tag); + Marshaler marshaler = getMarshalerForKey(key, nativeType); + int size = marshaler.calculateMarshalSize(value); - // TODO: Optimization. Cache the byte[] and reuse if the size is big enough. - byte[] values = new byte[size]; + // TODO: Optimization. Cache the byte[] and reuse if the size is big enough. + byte[] values = new byte[size]; - ByteBuffer buffer = ByteBuffer.wrap(values).order(ByteOrder.nativeOrder()); - marshaler.marshal(value, buffer); + ByteBuffer buffer = ByteBuffer.wrap(values).order(ByteOrder.nativeOrder()); + marshaler.marshal(value, buffer); - writeValues(tag, values); + writeValues(tag, values); + } catch (Exception e) { + // Do nothing + } } // Use Command pattern here to avoid lots of expensive if/equals checks in get for overridden diff --git a/core/java/android/hardware/camera2/impl/CaptureResultExtras.java b/core/java/android/hardware/camera2/impl/CaptureResultExtras.java index 8bf94986a4905..0257ab979cb1f 100644 --- a/core/java/android/hardware/camera2/impl/CaptureResultExtras.java +++ b/core/java/android/hardware/camera2/impl/CaptureResultExtras.java @@ -75,6 +75,20 @@ public CaptureResultExtras(int requestId, int subsequenceId, int afTriggerId, this.readoutTimestamp = readoutTimestamp; } + // Backwards-compatible constructor + public CaptureResultExtras(int requestId, int subsequenceId, int afTriggerId, + int precaptureTriggerId, long frameNumber, + int partialResultCount, int errorStreamId, + String errorPhysicalCameraId, long lastCompletedRegularFrameNumber, + long lastCompletedReprocessFrameNumber, + long lastCompletedZslFrameNumber) { + this(requestId, subsequenceId, afTriggerId, precaptureTriggerId, frameNumber, + partialResultCount, errorStreamId, errorPhysicalCameraId, + lastCompletedRegularFrameNumber, lastCompletedReprocessFrameNumber, + lastCompletedZslFrameNumber, + false /*hasReadOutTimestamp*/, 0 /*readoutTimestamp*/); + } + @Override public int describeContents() { return 0; diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java index 639612b3dfe6b..3bc70312d0bec 100644 --- a/core/java/android/hardware/camera2/params/OutputConfiguration.java +++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java @@ -1167,6 +1167,21 @@ public boolean isDeferredConfiguration() { return mIsDeferredConfig; } + /** + * Update the configured surface size to the latest surface size. + * This function must be called right before any binder call sent to cameraserver + * that references OutputConfiguration. + * This is needed in order to update the cached surface size which may have changed + * after OutputConfiguration's construction. + * + * @hide + */ + public void updateCachedSurfaceSize() { + Surface surface = getSurface(); + if (surface != null) { + mConfiguredSize = SurfaceUtils.getSurfaceSize(surface); + } + } /** * Add a surface to this OutputConfiguration. * @@ -1719,12 +1734,13 @@ public int getSurfaceGroupId() { } /** - * Get the configured size associated with this {@link OutputConfiguration}. + * Get the latest configured size associated with this {@link OutputConfiguration}. * * @return The configured size associated with this {@link OutputConfiguration}. */ @FlaggedApi(Flags.FLAG_OUTPUT_CONFIGURATION_GETTER) public @NonNull Size getConfiguredSize() { + updateCachedSurfaceSize(); return mConfiguredSize; } @@ -1858,8 +1874,12 @@ public void writeToParcel(Parcel dest, int flags) { dest.writeInt(mRotation); dest.writeInt(mSurfaceGroupId); dest.writeInt(mSurfaceType); - dest.writeInt(mConfiguredSize.getWidth()); - dest.writeInt(mConfiguredSize.getHeight()); + // Note: If / When OutputConfiguration becomes an auto-generated Parcelable, + // OutputConfiguration.updateCachedSurfaceSize() needs to + // be called before calling any binder call that references OutputConfiguration. + Size configuredSize = getConfiguredSize(); + dest.writeInt(configuredSize.getWidth()); + dest.writeInt(configuredSize.getHeight()); dest.writeInt(mIsDeferredConfig ? 1 : 0); dest.writeInt(mIsShared ? 1 : 0); dest.writeTypedList(mSurfaces); @@ -2006,7 +2026,7 @@ public int hashCode() { private final int mSurfaceType; // The size, format, and dataspace of the surface when OutputConfiguration is created. - private final Size mConfiguredSize; + private Size mConfiguredSize; private final int mConfiguredFormat; private final int mConfiguredDataspace; // The public facing format, a combination of mConfiguredFormat and mConfiguredDataspace diff --git a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java index ad55ec715272e..46fec6d0fdb14 100644 --- a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java +++ b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java @@ -151,6 +151,136 @@ public StreamConfigurationMap( listHighResolution, /*enforceImplementationDefined*/ true); } + /** + * Create a new {@link StreamConfigurationMap}. + * + *

The array parameters ownership is passed to this object after creation; do not + * write to them after this constructor is invoked.

+ * + * @param configurations a non-{@code null} array of {@link StreamConfiguration} + * @param minFrameDurations a non-{@code null} array of {@link StreamConfigurationDuration} + * @param stallDurations a non-{@code null} array of {@link StreamConfigurationDuration} + * @param depthConfigurations a non-{@code null} array of depth {@link StreamConfiguration} + * @param depthMinFrameDurations a non-{@code null} array of depth + * {@link StreamConfigurationDuration} + * @param depthStallDurations a non-{@code null} array of depth + * {@link StreamConfigurationDuration} + * @param dynamicDepthConfigurations a non-{@code null} array of dynamic depth + * {@link StreamConfiguration} + * @param dynamicDepthMinFrameDurations a non-{@code null} array of dynamic depth + * {@link StreamConfigurationDuration} + * @param dynamicDepthStallDurations a non-{@code null} array of dynamic depth + * {@link StreamConfigurationDuration} + * @param heicConfigurations a non-{@code null} array of heic {@link StreamConfiguration} + * @param heicMinFrameDurations a non-{@code null} array of heic + * {@link StreamConfigurationDuration} + * @param heicStallDurations a non-{@code null} array of heic + * {@link StreamConfigurationDuration} + * @param highSpeedVideoConfigurations an array of {@link HighSpeedVideoConfiguration}, null if + * camera device does not support high speed video recording + * @param listHighResolution a flag indicating whether the device supports BURST_CAPTURE + * and thus needs a separate list of slow high-resolution output sizes + * @throws NullPointerException if any of the arguments except highSpeedVideoConfigurations + * were {@code null} or any subelements were {@code null} + * + * @hide + */ + public StreamConfigurationMap( + StreamConfiguration[] configurations, + StreamConfigurationDuration[] minFrameDurations, + StreamConfigurationDuration[] stallDurations, + StreamConfiguration[] depthConfigurations, + StreamConfigurationDuration[] depthMinFrameDurations, + StreamConfigurationDuration[] depthStallDurations, + StreamConfiguration[] dynamicDepthConfigurations, + StreamConfigurationDuration[] dynamicDepthMinFrameDurations, + StreamConfigurationDuration[] dynamicDepthStallDurations, + StreamConfiguration[] heicConfigurations, + StreamConfigurationDuration[] heicMinFrameDurations, + StreamConfigurationDuration[] heicStallDurations, + HighSpeedVideoConfiguration[] highSpeedVideoConfigurations, + ReprocessFormatsMap inputOutputFormatsMap, + boolean listHighResolution) { + this(configurations, minFrameDurations, stallDurations, + depthConfigurations, depthMinFrameDurations, depthStallDurations, + dynamicDepthConfigurations, dynamicDepthMinFrameDurations, + dynamicDepthStallDurations, + heicConfigurations, heicMinFrameDurations, heicStallDurations, + null /*jpegRConfigurations*/, null /*jpegRMinFrameDurations*/, null /*jpegRStallDurations*/, + null /*heicUltraHDRConfigurations*/, null /*heicUltraHDRMinFrameDurations*/, + null /*heicUltraHDRStallDurations*/, highSpeedVideoConfigurations, inputOutputFormatsMap, + listHighResolution, /*enforceImplementationDefined*/ true); + } + + /** + * Create a new {@link StreamConfigurationMap}. + * + *

The array parameters ownership is passed to this object after creation; do not + * write to them after this constructor is invoked.

+ * + * @param configurations a non-{@code null} array of {@link StreamConfiguration} + * @param minFrameDurations a non-{@code null} array of {@link StreamConfigurationDuration} + * @param stallDurations a non-{@code null} array of {@link StreamConfigurationDuration} + * @param depthConfigurations a non-{@code null} array of depth {@link StreamConfiguration} + * @param depthMinFrameDurations a non-{@code null} array of depth + * {@link StreamConfigurationDuration} + * @param depthStallDurations a non-{@code null} array of depth + * {@link StreamConfigurationDuration} + * @param dynamicDepthConfigurations a non-{@code null} array of dynamic depth + * {@link StreamConfiguration} + * @param dynamicDepthMinFrameDurations a non-{@code null} array of dynamic depth + * {@link StreamConfigurationDuration} + * @param dynamicDepthStallDurations a non-{@code null} array of dynamic depth + * {@link StreamConfigurationDuration} + * @param heicConfigurations a non-{@code null} array of heic {@link StreamConfiguration} + * @param heicMinFrameDurations a non-{@code null} array of heic + * {@link StreamConfigurationDuration} + * @param heicStallDurations a non-{@code null} array of heic + * {@link StreamConfigurationDuration} + * @param jpegRConfigurations a non-{@code null} array of Jpeg/R {@link StreamConfiguration} + * @param jpegRMinFrameDurations a non-{@code null} array of Jpeg/R + * {@link StreamConfigurationDuration} + * @param jpegRStallDurations a non-{@code null} array of Jpeg/R + * {@link StreamConfigurationDuration} + * @param highSpeedVideoConfigurations an array of {@link HighSpeedVideoConfiguration}, null if + * camera device does not support high speed video recording + * @param listHighResolution a flag indicating whether the device supports BURST_CAPTURE + * and thus needs a separate list of slow high-resolution output sizes + * @throws NullPointerException if any of the arguments except highSpeedVideoConfigurations + * were {@code null} or any subelements were {@code null} + * + * @hide + */ + public StreamConfigurationMap( + StreamConfiguration[] configurations, + StreamConfigurationDuration[] minFrameDurations, + StreamConfigurationDuration[] stallDurations, + StreamConfiguration[] depthConfigurations, + StreamConfigurationDuration[] depthMinFrameDurations, + StreamConfigurationDuration[] depthStallDurations, + StreamConfiguration[] dynamicDepthConfigurations, + StreamConfigurationDuration[] dynamicDepthMinFrameDurations, + StreamConfigurationDuration[] dynamicDepthStallDurations, + StreamConfiguration[] heicConfigurations, + StreamConfigurationDuration[] heicMinFrameDurations, + StreamConfigurationDuration[] heicStallDurations, + StreamConfiguration[] jpegRConfigurations, + StreamConfigurationDuration[] jpegRMinFrameDurations, + StreamConfigurationDuration[] jpegRStallDurations, + HighSpeedVideoConfiguration[] highSpeedVideoConfigurations, + ReprocessFormatsMap inputOutputFormatsMap, + boolean listHighResolution) { + this(configurations, minFrameDurations, stallDurations, + depthConfigurations, depthMinFrameDurations, depthStallDurations, + dynamicDepthConfigurations, dynamicDepthMinFrameDurations, + dynamicDepthStallDurations, + heicConfigurations, heicMinFrameDurations, heicStallDurations, + jpegRConfigurations, jpegRMinFrameDurations, jpegRStallDurations, + null /*heicUltraHDRConfigurations*/, null /*heicUltraHDRMinFrameDurations*/, + null /*heicUltraHDRStallDurations*/, highSpeedVideoConfigurations, inputOutputFormatsMap, + listHighResolution, /*enforceImplementationDefined*/ true); + } + /** * Create a new {@link StreamConfigurationMap}. * diff --git a/core/java/android/hardware/camera2/utils/SurfaceUtils.java b/core/java/android/hardware/camera2/utils/SurfaceUtils.java index 6c83057fdf297..a624a3bbc8117 100644 --- a/core/java/android/hardware/camera2/utils/SurfaceUtils.java +++ b/core/java/android/hardware/camera2/utils/SurfaceUtils.java @@ -20,11 +20,14 @@ import static com.android.internal.util.Preconditions.checkNotNull; +import android.app.ActivityThread; import android.compat.annotation.UnsupportedAppUsage; import android.graphics.ImageFormat; import android.graphics.PixelFormat; import android.hardware.HardwareBuffer; import android.hardware.camera2.params.StreamConfigurationMap; +import android.os.SystemProperties; +import android.text.TextUtils; import android.util.Range; import android.util.Size; import android.view.Surface; @@ -241,6 +244,11 @@ public static void checkConstrainedHighSpeedSurfaces(Collection surface + " the size must be 1 or 2"); } + if (isPrivilegedApp()) { + //skip checks for privileged apps + return; + } + List highSpeedSizes = null; if (fpsRange == null) { highSpeedSizes = Arrays.asList(config.getHighSpeedVideoSizes()); @@ -303,4 +311,21 @@ private static native int nativeDetectSurfaceDimens(Surface surface, /*out*/int[/*2*/] dimens); private static native long nativeGetSurfaceId(Surface surface); + + private static boolean isPrivilegedApp() { + String packageName = ActivityThread.currentOpPackageName(); + String packageList = SystemProperties.get("persist.vendor.camera.privapp.list"); + + if (packageList.length() > 0) { + TextUtils.StringSplitter splitter = new TextUtils.SimpleStringSplitter(','); + splitter.setString(packageList); + for (String str : splitter) { + if (packageName.equals(str)) { + return true; + } + } + } + + return false; + } } diff --git a/core/java/android/hardware/display/AmbientDisplayConfiguration.java b/core/java/android/hardware/display/AmbientDisplayConfiguration.java index 063f5545dd71f..b8d9148338b02 100644 --- a/core/java/android/hardware/display/AmbientDisplayConfiguration.java +++ b/core/java/android/hardware/display/AmbientDisplayConfiguration.java @@ -20,7 +20,10 @@ import android.annotation.TestApi; import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; import android.hardware.biometrics.Flags; +import android.os.BatteryManager; import android.os.Build; import android.os.SystemProperties; import android.provider.Settings; @@ -40,11 +43,13 @@ */ @TestApi public class AmbientDisplayConfiguration { - private static final String TAG = "AmbientDisplayConfig"; + private static final IntentFilter sIntentFilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED); + private final Context mContext; private final boolean mAlwaysOnByDefault; private final boolean mPickupGestureEnabledByDefault; private final boolean mScreenOffUdfpsAvailable; + private final boolean mDozeEnabledByDefault; /** Copied from android.provider.Settings.Secure since these keys are hidden. */ private static final String[] DOZE_SETTINGS = { @@ -55,7 +60,8 @@ public class AmbientDisplayConfiguration { Settings.Secure.DOZE_DOUBLE_TAP_GESTURE, Settings.Secure.DOZE_WAKE_LOCK_SCREEN_GESTURE, Settings.Secure.DOZE_WAKE_DISPLAY_GESTURE, - Settings.Secure.DOZE_TAP_SCREEN_GESTURE + Settings.Secure.DOZE_TAP_SCREEN_GESTURE, + Settings.Secure.DOZE_ON_CHARGE }; /** Non-user configurable doze settings */ @@ -74,6 +80,8 @@ public AmbientDisplayConfiguration(Context context) { mContext.getResources().getBoolean(R.bool.config_dozePickupGestureEnabled); mScreenOffUdfpsAvailable = mContext.getResources().getBoolean(R.bool.config_screen_off_udfps_enabled); + mDozeEnabledByDefault = + mContext.getResources().getBoolean(R.bool.config_doze_enabled_by_default); } /** @hide */ @@ -81,9 +89,14 @@ public boolean enabled(int user) { return pulseOnNotificationEnabled(user) || pulseOnLongPressEnabled(user) || alwaysOnEnabled(user) + || edgeLightEnabled(user) + || isAmbientTickerEnabled(user) || wakeLockScreenGestureEnabled(user) || wakeDisplayGestureEnabled(user) || pickupGestureEnabled(user) + || tiltGestureEnabled(user) + || handwaveGestureEnabled(user) + || pocketGestureEnabled(user) || tapGestureEnabled(user) || doubleTapGestureEnabled(user) || quickPickupSensorEnabled(user) @@ -92,7 +105,7 @@ public boolean enabled(int user) { /** @hide */ public boolean pulseOnNotificationEnabled(int user) { - return boolSettingDefaultOn(Settings.Secure.DOZE_ENABLED, user) + return boolSetting(Settings.Secure.DOZE_ENABLED, user, mDozeEnabledByDefault ? 1 : 0) && pulseOnNotificationAvailable(); } @@ -102,6 +115,11 @@ public boolean pulseOnNotificationAvailable() { && ambientDisplayAvailable(); } + /** @hide */ + public boolean isAmbientTickerEnabled(int user) { + return boolSettingDefaultOff(Settings.Secure.PULSE_ON_NEW_TRACKS, user); + } + /** @hide */ public boolean pickupGestureEnabled(int user) { return boolSetting(Settings.Secure.DOZE_PICK_UP_GESTURE, user, @@ -114,6 +132,40 @@ public boolean dozePickupSensorAvailable() { return mContext.getResources().getBoolean(R.bool.config_dozePulsePickup); } + /** {@hide} */ + public boolean tiltGestureEnabled(int user) { + return boolSettingDefaultOff(Settings.Secure.DOZE_TILT_GESTURE, user) + && dozeTiltSensorAvailable(); + } + + /** {@hide} */ + public boolean dozeTiltSensorAvailable() { + return mContext.getResources().getBoolean(R.bool.config_dozePulseTilt); + } + + /** {@hide} */ + public boolean handwaveGestureEnabled(int user) { + return boolSettingDefaultOff(Settings.Secure.DOZE_HANDWAVE_GESTURE, user) + && dozeProximitySensorAvailable(); + } + + /** {@hide} */ + public boolean pocketGestureEnabled(int user) { + return boolSettingDefaultOff(Settings.Secure.DOZE_POCKET_GESTURE, user) + && dozeProximitySensorAvailable(); + } + + /** {@hide} */ + public boolean dozeProximitySensorAvailable() { + return mContext.getResources().getBoolean(R.bool.config_dozePulseProximity); + } + + /** @hide */ + public boolean edgeLightEnabled(int user) { + return Settings.System.getIntForUser(mContext.getContentResolver(), + Settings.System.EDGE_LIGHT_ENABLED, 0, user) != 0; + } + /** @hide */ public boolean tapGestureEnabled(int user) { return boolSettingDefaultOn(Settings.Secure.DOZE_TAP_SCREEN_GESTURE, user) @@ -151,11 +203,10 @@ && pickupGestureEnabled(user) /** @hide */ public boolean screenOffUdfpsEnabled(int user) { - return !TextUtils.isEmpty(udfpsLongPressSensorType()) - && ((mScreenOffUdfpsAvailable && Flags.screenOffUnlockUdfps()) - && mContext.getResources().getBoolean(R.bool.config_screen_off_udfps_default_on) + if (!mScreenOffUdfpsAvailable) return false; + return mContext.getResources().getBoolean(R.bool.config_screen_off_udfps_default_on) ? boolSettingDefaultOn(SCREEN_OFF_UNLOCK_UDFPS_ENABLED, user) - : boolSettingDefaultOff(SCREEN_OFF_UNLOCK_UDFPS_ENABLED, user)); + : boolSettingDefaultOff(SCREEN_OFF_UNLOCK_UDFPS_ENABLED, user); } /** @hide */ @@ -232,8 +283,36 @@ private boolean pulseOnLongPressAvailable() { */ @TestApi public boolean alwaysOnEnabled(int user) { - return boolSetting(Settings.Secure.DOZE_ALWAYS_ON, user, mAlwaysOnByDefault ? 1 : 0) - && alwaysOnAvailable() && !accessibilityInversionEnabled(user); + return alwaysOnEnabledSetting(user) || alwaysOnChargingEnabled(user); + } + + public boolean alwaysOnEnabledSetting(int user) { + final boolean alwaysOnEnabled = Settings.Secure.getIntForUser( + mContext.getContentResolver(), Settings.Secure.DOZE_ALWAYS_ON, + mAlwaysOnByDefault ? 1 : 0, user) == 1; + return alwaysOnEnabled && alwaysOnAvailable() && !accessibilityInversionEnabled(user); + } + + public boolean alwaysOnChargingEnabledSetting(int user) { + return Settings.Secure.getIntForUser(mContext.getContentResolver(), + Settings.Secure.DOZE_ON_CHARGE, 0, user) == 1; + } + + private boolean alwaysOnChargingEnabled(int user) { + if (alwaysOnChargingEnabledSetting(user)) { + final Intent intent = mContext.registerReceiver(null, sIntentFilter, Context.RECEIVER_NOT_EXPORTED); + if (intent != null) { + int status = intent.getIntExtra(BatteryManager.EXTRA_STATUS, -1); + boolean isCharging = status == BatteryManager.BATTERY_STATUS_CHARGING || + status == BatteryManager.BATTERY_STATUS_FULL; + int plugged = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1); + boolean isPlugged = plugged == BatteryManager.BATTERY_PLUGGED_AC || + plugged == BatteryManager.BATTERY_PLUGGED_USB || + plugged == BatteryManager.BATTERY_PLUGGED_WIRELESS; + return isPlugged && isCharging; + } + } + return false; } /** diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java index f73bbdb55441d..6854d8726b866 100644 --- a/core/java/android/hardware/display/DisplayManagerInternal.java +++ b/core/java/android/hardware/display/DisplayManagerInternal.java @@ -24,17 +24,21 @@ import android.hardware.SensorManager; import android.hardware.input.HostUsiVersion; import android.os.Handler; +import android.os.IBinder; import android.os.PowerManager; import android.util.IntArray; import android.util.SparseArray; import android.view.Display; import android.view.DisplayInfo; +import android.view.Surface; import android.view.SurfaceControl; import android.view.SurfaceControl.RefreshRateRange; import android.view.SurfaceControl.Transaction; import android.window.DisplayWindowPolicyController; import android.window.ScreenCapture; +import com.libremobileos.freeform.ILMOFreeformDisplayCallback; + import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.List; @@ -464,6 +468,17 @@ public abstract RefreshRateRange getRefreshRateForDisplayAndSensor( */ public abstract IntArray getDisplayIds(); + // LMOFreeform + public abstract void createFreeformLocked(String name, ILMOFreeformDisplayCallback callback, + int width, int height, int densityDpi, boolean secure, boolean ownContentOnly, + boolean shouldShowSystemDecorations, Surface surface, float refreshRate, + long presentationDeadlineNanos); + + public abstract void resizeFreeform(IBinder appToken, int width, int height, + int densityDpi); + + public abstract void releaseFreeform(IBinder appToken); + /** * Get group id for given display id */ diff --git a/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java b/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java index 8fbe05c4e9eb1..7ea88a1c5ed50 100644 --- a/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java +++ b/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java @@ -40,6 +40,7 @@ public class FingerprintSensorPropertiesInternal extends SensorPropertiesInterna */ public final @FingerprintSensorProperties.SensorType int sensorType; public final boolean halControlsIllumination; + public final boolean halHandlesDisplayTouches; private final List mSensorLocations; @@ -48,6 +49,7 @@ public FingerprintSensorPropertiesInternal(int sensorId, @NonNull List componentInfo, @FingerprintSensorProperties.SensorType int sensorType, boolean halControlsIllumination, + boolean halHandlesDisplayTouches, boolean resetLockoutRequiresHardwareAuthToken, @NonNull List sensorLocations) { // IBiometricsFingerprint@2.1 handles lockout in the framework, so the challenge is not @@ -58,6 +60,7 @@ public FingerprintSensorPropertiesInternal(int sensorId, resetLockoutRequiresHardwareAuthToken, false /* resetLockoutRequiresChallenge */); this.sensorType = sensorType; this.halControlsIllumination = halControlsIllumination; + this.halHandlesDisplayTouches = halHandlesDisplayTouches; this.mSensorLocations = List.copyOf(sensorLocations); } @@ -71,7 +74,8 @@ public FingerprintSensorPropertiesInternal(int sensorId, boolean resetLockoutRequiresHardwareAuthToken) { // TODO(b/179175438): Value should be provided from the HAL this(sensorId, strength, maxEnrollmentsPerUser, componentInfo, sensorType, - false /* halControlsIllumination */, resetLockoutRequiresHardwareAuthToken, + false /* halControlsIllumination */, false /* halHandlesDisplayTouches */, + resetLockoutRequiresHardwareAuthToken, List.of(new SensorLocationInternal("" /* displayId */, 540 /* sensorLocationX */, 1636 /* sensorLocationY */, 130 /* sensorRadius */))); } @@ -80,6 +84,7 @@ protected FingerprintSensorPropertiesInternal(Parcel in) { super(in); sensorType = in.readInt(); halControlsIllumination = in.readBoolean(); + halHandlesDisplayTouches = in.readBoolean(); mSensorLocations = in.createTypedArrayList(SensorLocationInternal.CREATOR); } @@ -106,6 +111,7 @@ public void writeToParcel(Parcel dest, int flags) { super.writeToParcel(dest, flags); dest.writeInt(sensorType); dest.writeBoolean(halControlsIllumination); + dest.writeBoolean(halHandlesDisplayTouches); dest.writeTypedList(mSensorLocations); } diff --git a/core/java/android/hardware/fingerprint/HidlFingerprintSensorConfig.java b/core/java/android/hardware/fingerprint/HidlFingerprintSensorConfig.java index d481153fc6426..95721b76539f9 100644 --- a/core/java/android/hardware/fingerprint/HidlFingerprintSensorConfig.java +++ b/core/java/android/hardware/fingerprint/HidlFingerprintSensorConfig.java @@ -17,7 +17,9 @@ package android.hardware.fingerprint; import android.annotation.NonNull; +import android.annotation.Nullable; import android.content.Context; +import android.content.res.TypedArray; import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.BiometricManager; import android.hardware.biometrics.common.CommonProps; @@ -25,16 +27,22 @@ import android.hardware.biometrics.fingerprint.FingerprintSensorType; import android.hardware.biometrics.fingerprint.SensorLocation; import android.hardware.biometrics.fingerprint.SensorProps; +import android.util.Slog; import com.android.internal.R; import com.android.internal.util.ArrayUtils; +import java.util.ArrayList; +import java.util.List; + /** * Parse HIDL fingerprint sensor config and map it to SensorProps.aidl to match AIDL. * See core/res/res/values/config.xml config_biometric_sensors * @hide */ public final class HidlFingerprintSensorConfig extends SensorProps { + private static final String TAG = "HidlFingerprintSensorConfig"; + private int mSensorId; private int mModality; private int mStrength; @@ -68,8 +76,13 @@ private void mapHidlToAidlSensorConfiguration(@NonNull Context context) { commonProps.maxEnrollmentsPerUser = context.getResources().getInteger( R.integer.config_fingerprintMaxTemplatesPerUser); halControlsIllumination = false; + halHandlesDisplayTouches = false; sensorLocations = new SensorLocation[1]; + // Non-empty workaroundLocations indicates that the sensor is SFPS. + final List workaroundLocations = + getWorkaroundSensorProps(context); + final int[] udfpsProps = context.getResources().getIntArray( com.android.internal.R.array.config_udfps_sensor_props); final boolean isUdfps = !ArrayUtils.isEmpty(udfpsProps); @@ -78,7 +91,7 @@ private void mapHidlToAidlSensorConfiguration(@NonNull Context context) { R.bool.config_is_powerbutton_fps); if (isUdfps) { - sensorType = FingerprintSensorType.UNKNOWN; + sensorType = FingerprintSensorType.UNDER_DISPLAY_OPTICAL; } else if (isPowerbuttonFps) { sensorType = FingerprintSensorType.POWER_BUTTON; } else { @@ -87,6 +100,9 @@ private void mapHidlToAidlSensorConfiguration(@NonNull Context context) { if (isUdfps && udfpsProps.length == 3) { setSensorLocation(udfpsProps[0], udfpsProps[1], udfpsProps[2]); + } else if (!workaroundLocations.isEmpty()) { + sensorLocations = new SensorLocation[workaroundLocations.size()]; + workaroundLocations.toArray(sensorLocations); } else { setSensorLocation(540 /* sensorLocationX */, 1636 /* sensorLocationY */, 130 /* sensorRadius */); @@ -103,6 +119,48 @@ private void setSensorLocation(int sensorLocationX, sensorLocations[0].sensorRadius = sensorRadius; } + // TODO(b/174868353): workaround for gaps in HAL interface (remove and get directly from HAL) + // reads values via an overlay instead of querying the HAL + @NonNull + public static List getWorkaroundSensorProps(@NonNull Context context) { + final List sensorLocations = new ArrayList<>(); + + final TypedArray sfpsProps = context.getResources().obtainTypedArray( + com.android.internal.R.array.config_sfps_sensor_props); + for (int i = 0; i < sfpsProps.length(); i++) { + final int id = sfpsProps.getResourceId(i, -1); + if (id > 0) { + final SensorLocation location = parseSensorLocation( + context.getResources().obtainTypedArray(id)); + if (location != null) { + sensorLocations.add(location); + } + } + } + sfpsProps.recycle(); + + return sensorLocations; + } + + @Nullable + private static SensorLocation parseSensorLocation(@Nullable TypedArray array) { + if (array == null) { + return null; + } + + try { + SensorLocation sensorLocation = new SensorLocation(); + sensorLocation.display = array.getString(0); + sensorLocation.sensorLocationX = array.getInt(1, 0); + sensorLocation.sensorLocationY = array.getInt(2, 0); + sensorLocation.sensorRadius = array.getInt(3, 0); + return sensorLocation; + } catch (Exception e) { + Slog.w(TAG, "malformed sensor location", e); + } + return null; + } + private byte authenticatorStrengthToPropertyStrength( @BiometricManager.Authenticators.Types int strength) { switch (strength) { diff --git a/core/java/android/inputmethodservice/IInputMethodWrapper.java b/core/java/android/inputmethodservice/IInputMethodWrapper.java index 8e782d1b190db..de3bf3107c939 100644 --- a/core/java/android/inputmethodservice/IInputMethodWrapper.java +++ b/core/java/android/inputmethodservice/IInputMethodWrapper.java @@ -437,6 +437,10 @@ public void createSession(InputChannel channel, IInputMethodSessionCallback call @BinderThread @Override public void setSessionEnabled(IInputMethodSession session, boolean enabled) { + if (session == null) { + Log.w(TAG, "Incoming session is null"); + return; + } try { InputMethodSession ls = ((IInputMethodSessionWrapper) session).getInternalInputMethodSession(); diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index e54fad119313b..d34737258a079 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -658,6 +658,11 @@ public class InputMethodService extends AbstractInputMethodService { @Nullable private ImeTracker.Token mCurStatsToken; + int mVolumeKeyCursorControl; + private static final int VOLUME_CURSOR_OFF = 0; + private static final int VOLUME_CURSOR_ON = 1; + private static final int VOLUME_CURSOR_ON_REVERSE = 2; + final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsComputer = info -> { onComputeInsets(mTmpInsets); mNavigationBarController.updateInsets(mTmpInsets); @@ -1604,6 +1609,9 @@ public static SettingsObserver createAndRegister(InputMethodService service) { service.getContentResolver().registerContentObserver( Settings.Secure.getUriFor(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD), false, observer); + service.getContentResolver().registerContentObserver( + Settings.System.getUriFor(Settings.System.VOLUME_KEY_CURSOR_CONTROL), + false, observer); return observer; } @@ -1644,6 +1652,9 @@ public void onChange(boolean selfChange, Uri uri) { // state as if configuration was changed. mService.resetStateForNewConfiguration(); } + + mService.mVolumeKeyCursorControl = Settings.System.getInt(mService.getContentResolver(), + Settings.System.VOLUME_KEY_CURSOR_CONTROL, 0); } @Override @@ -1726,6 +1737,8 @@ public boolean enableHardwareAcceleration() { // cache preference so we don't have to read ContentProvider when IME is requested to be // shown the first time (cold start). mSettingsObserver.shouldShowImeWithHardKeyboard(); + mVolumeKeyCursorControl = Settings.System.getInt(getContentResolver(), + Settings.System.VOLUME_KEY_CURSOR_CONTROL, 0); final boolean hideNavBarForKeyboard = getApplicationContext().getResources().getBoolean( com.android.internal.R.bool.config_hideNavBarForKeyboard); @@ -3693,6 +3706,23 @@ public boolean onKeyDown(int keyCode, KeyEvent event) { } } + if (event.getKeyCode() == KeyEvent.KEYCODE_VOLUME_UP) { + if (isInputViewShown() && mVolumeKeyCursorControl != VOLUME_CURSOR_OFF) { + sendDownUpKeyEvents(mVolumeKeyCursorControl == VOLUME_CURSOR_ON_REVERSE + ? KeyEvent.KEYCODE_DPAD_RIGHT : KeyEvent.KEYCODE_DPAD_LEFT); + return true; + } + return false; + } + if (event.getKeyCode() == KeyEvent.KEYCODE_VOLUME_DOWN) { + if (isInputViewShown() && mVolumeKeyCursorControl != VOLUME_CURSOR_OFF) { + sendDownUpKeyEvents(mVolumeKeyCursorControl == VOLUME_CURSOR_ON_REVERSE + ? KeyEvent.KEYCODE_DPAD_LEFT : KeyEvent.KEYCODE_DPAD_RIGHT); + return true; + } + return false; + } + return doMovementKey(keyCode, event, MOVEMENT_DOWN); } @@ -3785,6 +3815,11 @@ public boolean onKeyUp(int keyCode, KeyEvent event) { } } + if (event.getKeyCode() == KeyEvent.KEYCODE_VOLUME_UP + || keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) { + return isInputViewShown() && mVolumeKeyCursorControl != VOLUME_CURSOR_OFF; + } + return doMovementKey(keyCode, event, MOVEMENT_UP); } diff --git a/core/java/android/media/ImageReader.java b/core/java/android/media/ImageReader.java index 530d48d3e60b0..76a6b1504a4eb 100644 --- a/core/java/android/media/ImageReader.java +++ b/core/java/android/media/ImageReader.java @@ -37,6 +37,7 @@ import android.os.Handler; import android.os.Looper; import android.os.ParcelFileDescriptor; +import android.os.SystemProperties; import android.os.Trace; import android.view.Surface; @@ -901,7 +902,9 @@ public void detachImage(@Nullable Image image) { throw new IllegalStateException("Image was already detached from this ImageReader"); } - nativeDetachImage(image, mDetachThrowsIseOnly); + if (!SystemProperties.getBoolean("persist.sys.cam.skip_detach_image", false)) { + nativeDetachImage(image, mDetachThrowsIseOnly); + } si.clearSurfacePlanes(); si.mPlanes = null; si.setDetached(true); diff --git a/core/java/android/net/INetworkPolicyListener.aidl b/core/java/android/net/INetworkPolicyListener.aidl index 00c691379187b..307c426b00b5f 100644 --- a/core/java/android/net/INetworkPolicyListener.aidl +++ b/core/java/android/net/INetworkPolicyListener.aidl @@ -26,4 +26,5 @@ oneway interface INetworkPolicyListener { void onSubscriptionOverride(int subId, int overrideMask, int overrideValue, in int[] networkTypes); void onSubscriptionPlansChanged(int subId, in SubscriptionPlan[] plans); void onBlockedReasonChanged(int uid, int oldBlockedReason, int newBlockedReason); + void onAllowedTransportsChanged(in int[] uids, in long[] allowedTransports); } diff --git a/core/java/android/net/INetworkPolicyManager.aidl b/core/java/android/net/INetworkPolicyManager.aidl index 8f77571c94c74..d0684bb06ec26 100644 --- a/core/java/android/net/INetworkPolicyManager.aidl +++ b/core/java/android/net/INetworkPolicyManager.aidl @@ -94,4 +94,9 @@ interface INetworkPolicyManager { boolean isUidNetworkingBlocked(int uid, boolean meteredNetwork); @EnforcePermission("OBSERVE_NETWORK_POLICY") boolean isUidRestrictedOnMeteredNetworks(int uid); + + byte[] getBackupPayload(int user); + void applyRestore(in byte[] payload, int user); + + void notifyDenylistChanged(in int[] uidsAdded, in int[] uidsRemoved); } diff --git a/core/java/android/net/IVpnManager.aidl b/core/java/android/net/IVpnManager.aidl index 5149967ccac87..bce207c15e524 100644 --- a/core/java/android/net/IVpnManager.aidl +++ b/core/java/android/net/IVpnManager.aidl @@ -58,6 +58,7 @@ interface IVpnManager { /** Legacy VPN APIs */ void startLegacyVpn(in VpnProfile profile); LegacyVpnInfo getLegacyVpnInfo(int userId); + VpnProfile[] getAllLegacyVpns(); boolean updateLockdownVpn(); /** Profile store APIs */ diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java index 3e6bbf6cdd0c6..e5a4329794d93 100644 --- a/core/java/android/net/NetworkPolicyManager.java +++ b/core/java/android/net/NetworkPolicyManager.java @@ -88,6 +88,29 @@ public class NetworkPolicyManager { * @hide */ public static final int POLICY_ALLOW_METERED_BACKGROUND = 0x4; + /** Reject network usage on cellular network + * @hide + */ + public static final int POLICY_REJECT_CELLULAR = 0x10000; + /** Reject network usage on virtual private network + * @hide + */ + public static final int POLICY_REJECT_VPN = 0x20000; + /** Reject network usage on wifi network + * @hide + */ + public static final int POLICY_REJECT_WIFI = 0x8000; + /** Reject network usage on all networks + * @hide + */ + public static final int POLICY_REJECT_ALL = 0x40000; + + /** @hide */ + public static final int POLICY_LOCKDOWN_VPN = POLICY_REJECT_WIFI | POLICY_REJECT_CELLULAR; + + /** @hide */ + public static final int POLICY_LOCKDOWN_VPN_MASK = POLICY_REJECT_WIFI | POLICY_REJECT_CELLULAR + | POLICY_REJECT_VPN; /* * Rules defining whether an uid has access to a network given its type (metered / non-metered). @@ -321,6 +344,8 @@ public class NetworkPolicyManager { mSubscriptionCallbackMap = new ConcurrentHashMap<>(); private final Map mNetworkPolicyCallbackMap = new ConcurrentHashMap<>(); + private final Map + mAllowedTransportsCallbackMap = new ConcurrentHashMap<>(); /** @hide */ public NetworkPolicyManager(Context context, INetworkPolicyManager service) { @@ -407,6 +432,17 @@ public int[] getUidsWithPolicy(int policy) { } } + /** @hide */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + @RequiresPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) + public void notifyDenylistChanged(@NonNull int[] uidsAdded, @NonNull int[] uidsRemoved) { + try { + mService.notifyDenylistChanged(uidsAdded, uidsRemoved); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + /** @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) public void registerListener(INetworkPolicyListener listener) { @@ -1119,6 +1155,72 @@ public void onSubscriptionPlansChanged(int subId, SubscriptionPlan[] plans) { } } + /** @hide */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY) + public void registerAllowedTransportsCallback(@Nullable Executor executor, + @NonNull AllowedTransportsCallback callback) { + if (callback == null) { + throw new NullPointerException("Callback cannot be null."); + } + + final AllowedTransportsCallbackProxy callbackProxy = new AllowedTransportsCallbackProxy( + executor, callback); + registerListener(callbackProxy); + mAllowedTransportsCallbackMap.put(callback, callbackProxy); + } + + /** @hide */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY) + public void unregisterAllowedTransportsCallback(@NonNull AllowedTransportsCallback callback) { + if (callback == null) { + throw new NullPointerException("Callback cannot be null."); + } + + final AllowedTransportsCallbackProxy callbackProxy = + mAllowedTransportsCallbackMap.remove(callback); + if (callbackProxy == null) return; + unregisterListener(callbackProxy); + } + + /** @hide */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + public interface AllowedTransportsCallback { + /** @hide */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + default void onUidsAllowedTransportsChanged(@NonNull int[] uids, + @NonNull long[] allowedTransports) {} + } + + /** @hide */ + public static class AllowedTransportsCallbackProxy extends Listener { + private final Executor mExecutor; + private final AllowedTransportsCallback mCallback; + + AllowedTransportsCallbackProxy(@Nullable Executor executor, + @NonNull AllowedTransportsCallback callback) { + mExecutor = executor; + mCallback = callback; + } + + @Override + public void onAllowedTransportsChanged(int[] uids, long[] allowedTransports) { + dispatchOnUidsAllowedTransportsChanged(mExecutor, mCallback, uids, allowedTransports); + } + } + + private static void dispatchOnUidsAllowedTransportsChanged(@Nullable Executor executor, + @NonNull AllowedTransportsCallback callback, int[] uids, long[] allowedTransports) { + if (executor == null) { + callback.onUidsAllowedTransportsChanged(uids, allowedTransports); + } else { + executor.execute(PooledLambda.obtainRunnable( + AllowedTransportsCallback::onUidsAllowedTransportsChanged, + callback, uids, allowedTransports).recycleOnUse()); + } + } + /** {@hide} */ public static class Listener extends INetworkPolicyListener.Stub { @Override public void onUidRulesChanged(int uid, int uidRules) { } @@ -1130,5 +1232,6 @@ public static class Listener extends INetworkPolicyListener.Stub { @Override public void onSubscriptionPlansChanged(int subId, SubscriptionPlan[] plans) { } @Override public void onBlockedReasonChanged(int uid, int oldBlockedReasons, int newBlockedReasons) { } + @Override public void onAllowedTransportsChanged(int[] uids, long[] allowedTransports) { } } } diff --git a/core/java/android/net/VpnManager.java b/core/java/android/net/VpnManager.java index 4ef293a90a80a..6a6a159327115 100644 --- a/core/java/android/net/VpnManager.java +++ b/core/java/android/net/VpnManager.java @@ -816,4 +816,15 @@ public String[] listFromVpnProfileStore(@NonNull String prefix) { throw e.rethrowFromSystemServer(); } } + + /** + * @hide + */ + public VpnProfile[] getAllLegacyVpns() { + try { + return mService.getAllLegacyVpns(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } } diff --git a/core/java/android/os/BaseBundle.java b/core/java/android/os/BaseBundle.java index dd9e5697056c0..2e7a1aeaa061b 100644 --- a/core/java/android/os/BaseBundle.java +++ b/core/java/android/os/BaseBundle.java @@ -512,6 +512,13 @@ private void initializeFromParcelLocked(@NonNull Parcel parcelledData, boolean o } else { throw e; } + } catch (RuntimeException e) { + if (sShouldDefuse && (e.getCause() instanceof ClassNotFoundException)) { + Log.w(TAG, "Failed to parse Bundle, but defusing quietly", e); + map.erase(); + } else { + throw e; + } } finally { mWeakParcelledData = null; if (ownsParcel) { diff --git a/core/java/android/os/BatteryManager.java b/core/java/android/os/BatteryManager.java index b63ad5f0148ee..421443c49c765 100644 --- a/core/java/android/os/BatteryManager.java +++ b/core/java/android/os/BatteryManager.java @@ -166,6 +166,20 @@ public class BatteryManager { */ public static final String EXTRA_CHARGING_STATUS = "android.os.extra.CHARGING_STATUS"; + /** + * Extra for {@link android.content.Intent#ACTION_BATTERY_CHANGED}: + * Int value representing the estimated battery full charge capacity in microampere-hours. + * {@hide} + */ + public static final String EXTRA_MAXIMUM_CAPACITY = "android.os.extra.MAXIMUM_CAPACITY"; + + /** + * Extra for {@link android.content.Intent#ACTION_BATTERY_CHANGED}: + * Int value representing the battery full charge design capacity in microampere-hours. + * {@hide} + */ + public static final String EXTRA_DESIGN_CAPACITY = "android.os.extra.DESIGN_CAPACITY"; + /** * Battery capacity level is unsupported. * @@ -267,6 +281,13 @@ public class BatteryManager { @SystemApi public static final String EXTRA_EVENT_TIMESTAMP = "android.os.extra.EVENT_TIMESTAMP"; + /** + * Extra for {@link android.content.Intent#ACTION_BATTERY_CHANGED}: + * boolean value to detect fast charging + * {@hide} + */ + public static final String EXTRA_OEM_CHARGER = "oem_charger"; + // values for "status" field in the ACTION_BATTERY_CHANGED Intent public static final int BATTERY_STATUS_UNKNOWN = Constants.BATTERY_STATUS_UNKNOWN; public static final int BATTERY_STATUS_CHARGING = Constants.BATTERY_STATUS_CHARGING; diff --git a/core/java/android/os/CombinedMessageQueue/MessageQueue.java b/core/java/android/os/CombinedMessageQueue/MessageQueue.java index 840a504991477..346687d786519 100644 --- a/core/java/android/os/CombinedMessageQueue/MessageQueue.java +++ b/core/java/android/os/CombinedMessageQueue/MessageQueue.java @@ -197,16 +197,6 @@ private static boolean computeUseConcurrent() { } } - // Also explicitly allow SystemUI processes. - // SystemUI doesn't run in a core UID, but we want to give it the performance boost, - // and we know that it's safe to use the concurrent implementation in SystemUI. - if (processName.equals("com.android.systemui") - || processName.startsWith("com.android.systemui:")) { - return true; - } - // On Android distributions where SystemUI has a different process name, - // the above condition may need to be adjusted accordingly. - // We can lift these restrictions in the future after we've made it possible for test // authors to test Looper and MessageQueue without resorting to reflection. return false; diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java index 8184451910ffb..5d0ab946cddcc 100644 --- a/core/java/android/os/FileUtils.java +++ b/core/java/android/os/FileUtils.java @@ -1413,11 +1413,13 @@ public static boolean createDir(File dir) { public static long roundStorageSize(long size) { long val = 1; long pow = 1; - while ((val * pow) < size) { + long pow1024 = 1; + while ((val * pow1024) < size) { val <<= 1; if (val > 512) { val = 1; pow *= 1000; + pow1024 *= 1024; } } diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl index c95b2788f0552..f9b458c398110 100644 --- a/core/java/android/os/IPowerManager.aidl +++ b/core/java/android/os/IPowerManager.aidl @@ -182,4 +182,8 @@ interface IPowerManager const int GO_TO_SLEEP_REASON_MAX = 10; const int GO_TO_SLEEP_FLAG_NO_DOZE = 1 << 0; + // Lineage custom API + void rebootCustom(boolean confirm, String reason, boolean wait); + void setKeyboardVisibility(boolean visible); + void wakeUpWithProximityCheck(long time, int reason, String details, String opPackageName, int displayId); } diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java index 0b5e734552cc8..d0a416d70dde8 100644 --- a/core/java/android/os/PowerManager.java +++ b/core/java/android/os/PowerManager.java @@ -477,6 +477,15 @@ public static String userActivityEventToString(@UserActivityEvent int userActivi @SystemApi public static final int USER_ACTIVITY_FLAG_INDIRECT = 1 << 1; + /** + * @hide + * User activity flag: Certain hardware buttons are not supposed to + * activate hardware button illumination. This flag indicates a + * button event from one of those buttons. + * @hide + */ + public static final int USER_ACTIVITY_FLAG_NO_BUTTON_LIGHTS = 1 << 2; + /** * @hide */ @@ -623,7 +632,9 @@ public static String sleepReasonToString(@GoToSleepReason int sleepReason) { BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM, BRIGHTNESS_CONSTRAINT_TYPE_MAXIMUM, BRIGHTNESS_CONSTRAINT_TYPE_DEFAULT, - BRIGHTNESS_CONSTRAINT_TYPE_DIM + BRIGHTNESS_CONSTRAINT_TYPE_DIM, + BRIGHTNESS_CONSTRAINT_TYPE_DEFAULT_BUTTON, + BRIGHTNESS_CONSTRAINT_TYPE_DEFAULT_KEYBOARD }) @Retention(RetentionPolicy.SOURCE) public @interface BrightnessConstraint{} @@ -651,6 +662,18 @@ public static String sleepReasonToString(@GoToSleepReason int sleepReason) { */ public static final int BRIGHTNESS_CONSTRAINT_TYPE_DIM = 3; + /** + * Brightness constraint type: minimum allowed value. + * @hide + */ + public static final int BRIGHTNESS_CONSTRAINT_TYPE_DEFAULT_BUTTON = 8; + + /** + * Brightness constraint type: minimum allowed value. + * @hide + */ + public static final int BRIGHTNESS_CONSTRAINT_TYPE_DEFAULT_KEYBOARD = 9; + /** * @hide */ @@ -934,6 +957,27 @@ public int hashCode() { */ public static final String REBOOT_RECOVERY_UPDATE = "recovery-update"; + /** + * The value to pass as the 'reason' argument to reboot() to + * reboot into bootloader mode + * @hide + */ + public static final String REBOOT_BOOTLOADER = "bootloader"; + + /** + * The value to pass as the 'reason' argument to reboot() to + * reboot into download mode + * @hide + */ + public static final String REBOOT_DOWNLOAD = "download"; + + /** + * The value to pass as the 'reason' argument to reboot() to + * reboot into fastboot mode + * @hide + */ + public static final String REBOOT_FASTBOOT = "fastboot"; + /** * The value to pass as the 'reason' argument to reboot() when device owner requests a reboot on * the device. @@ -1762,6 +1806,24 @@ public void wakeUp(long time, @WakeReason int reason, String details, int displa } } + /** + * Forces the display with the supplied displayId to turn on only if nothing is blocking the + * proximity sensor. + * + * @see #wakeUp + * + * @hide + */ + public void wakeUpWithProximityCheck(long time, @WakeReason int reason, String details, + int displayId) { + try { + mService.wakeUpWithProximityCheck(time, reason, details, mContext.getOpPackageName(), + displayId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + /** * Forces the device to start napping. *

@@ -2059,6 +2121,24 @@ public void reboot(@Nullable String reason) { } } + /** + * Reboot the device. Will not return if the reboot is successful. + *

+ * Requires the {@link android.Manifest.permission#REBOOT} permission. + *

+ * + * @param reason code to pass to the kernel (e.g., "recovery", "bootloader", "download") to + * request special boot modes, or null. + * @hide + */ + public void rebootCustom(String reason) { + try { + mService.rebootCustom(false, reason, true); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + /** * Reboot the device. Will not return if the reboot is successful. *

@@ -3172,6 +3252,18 @@ public void removeThermalHeadroomListener(@NonNull OnThermalHeadroomChangedListe return ret; } + /** + * @hide + */ + public void setKeyboardVisibility(boolean visible) { + try { + if (mService != null) { + mService.setKeyboardVisibility(visible); + } + } catch (RemoteException e) { + } + } + /** * If true, the doze component is not started until after the screen has been * turned off and the screen off animation has been performed. diff --git a/core/java/android/os/PowerManagerInternal.java b/core/java/android/os/PowerManagerInternal.java index dbd1d7bce7737..849a80a6580f4 100644 --- a/core/java/android/os/PowerManagerInternal.java +++ b/core/java/android/os/PowerManagerInternal.java @@ -97,6 +97,16 @@ public static boolean isInteractive(int wakefulness) { return wakefulness == WAKEFULNESS_AWAKE || wakefulness == WAKEFULNESS_DREAMING; } + /** + * Used by the window manager to override the button brightness based on the + * current foreground activity. + * + * This method must only be called by the window manager. + * + * @param brightness The overridden brightness, or Float.NaN to disable the override. + */ + public abstract void setButtonBrightnessOverrideFromWindowManager(float brightness); + /** * Used by the window manager to override the user activity timeout based on the * current foreground activity. It can only be used to make the timeout shorter @@ -208,22 +218,12 @@ public interface LowPowerModeListener { public abstract void uidIdle(int uid); - /** - * Checks if the wakefulness of the supplied group is interactive. - */ - public abstract boolean isGroupInteractive(int groupId); - - /** Returns if any of the default adjacent group is interactive. */ - public abstract boolean isAnyDefaultAdjacentGroupInteractive(); - - /** Returns if the supplied group is adjacent to the default group. */ - public abstract boolean isDefaultGroupAdjacent(int groupId); - /** * Used to notify the power manager that wakelocks should be disabled. * * @param force {@code true} to activate force disable wakelocks, {@code false} to turn it off. */ + public abstract void setForceDisableWakelocks(boolean force); /** diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java index 398140dafc33e..709cb3fedaf65 100644 --- a/core/java/android/os/RecoverySystem.java +++ b/core/java/android/os/RecoverySystem.java @@ -37,6 +37,7 @@ import android.content.IntentSender; import android.content.pm.PackageManager; import android.hardware.display.DisplayManager; +import android.os.image.DynamicSystemManager; import android.provider.Settings; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; @@ -934,9 +935,15 @@ public static void rebootWipeUserData(Context context, boolean shutdown, String public static void rebootWipeUserData(Context context, boolean shutdown, String reason, boolean force, boolean wipeEuicc, boolean keepMemtagMode) throws IOException { UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE); + DynamicSystemManager dsuManager = (DynamicSystemManager) + context.getSystemService(Context.DYNAMIC_SYSTEM_SERVICE); if (!force && um.hasUserRestriction(UserManager.DISALLOW_FACTORY_RESET)) { throw new SecurityException("Wiping data is not allowed for this user."); } + + if (dsuManager.isInUse()) { + throw new SecurityException("Wiping data is not allowed while in DSU mode."); + } final ConditionVariable condition = new ConditionVariable(); Intent intent = new Intent("android.intent.action.MASTER_CLEAR_NOTIFICATION"); diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java index fe2be327a5a24..ca302c99c7463 100644 --- a/core/java/android/os/StrictMode.java +++ b/core/java/android/os/StrictMode.java @@ -1524,9 +1524,9 @@ public static void initThreadDefaults(ApplicationInfo ai) { builder.penaltyDeathOnNetwork(); } - if (Build.IS_USER || DISABLE || SystemProperties.getBoolean(DISABLE_PROPERTY, false)) { + if (Build.IS_USER || Build.IS_USERDEBUG || DISABLE || SystemProperties.getBoolean(DISABLE_PROPERTY, false)) { // Detect nothing extra - } else if (Build.IS_USERDEBUG || Build.IS_ENG) { + } else if (Build.IS_ENG) { // Detect everything in bundled apps if (isBundledSystemApp(ai)) { builder.detectAll(); @@ -1559,16 +1559,8 @@ public static void initVmDefaults(ApplicationInfo ai) { builder.penaltyDeathOnFileUriExposure(); } - if (Build.IS_USER || DISABLE || SystemProperties.getBoolean(DISABLE_PROPERTY, false)) { + if (Build.IS_USER || Build.IS_USERDEBUG || DISABLE || SystemProperties.getBoolean(DISABLE_PROPERTY, false)) { // Detect nothing extra - } else if (Build.IS_USERDEBUG) { - // Detect everything in bundled apps (except activity leaks, which - // are expensive to track) - if (isBundledSystemApp(ai)) { - builder.detectAll(); - builder.permitActivityLeaks(); - builder.penaltyDropBox(); - } } else if (Build.IS_ENG) { // Detect everything in bundled apps if (isBundledSystemApp(ai)) { @@ -2918,7 +2910,7 @@ protected IWindowManager create() { */ @UnsupportedAppUsage public static Span enterCriticalSpan(String name) { - if (Build.IS_USER) { + if (Build.IS_USER || Build.IS_USERDEBUG) { return NO_OP_SPAN; } if (name == null || name.isEmpty()) { diff --git a/core/java/android/os/UpdateEngine.java b/core/java/android/os/UpdateEngine.java index 81e4549c78d1e..a2740540f4636 100644 --- a/core/java/android/os/UpdateEngine.java +++ b/core/java/android/os/UpdateEngine.java @@ -466,6 +466,17 @@ public void resetShouldSwitchSlotOnReboot() { } } + /** + * @hide + */ + public void setPerformanceMode(boolean enable) { + try { + mUpdateEngine.setPerformanceMode(enable); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + /** * Unbinds the last bound callback function. */ diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java index b46fd8d504cda..079a598f5627a 100644 --- a/core/java/android/permission/PermissionManager.java +++ b/core/java/android/permission/PermissionManager.java @@ -255,6 +255,9 @@ public final class PermissionManager { private List mSplitPermissionInfos; + private static String[] sLocationProviderPkgNames; + private static String[] sLocationExtraPkgNames; + /** * Creates a new instance. * @@ -1358,6 +1361,16 @@ public static Set getIndicatorExemptedPackages(@NonNull Context context) pkgNames.add(exemptedPackage); } } + for (String pkgName: sLocationProviderPkgNames) { + if (pkgName != null) { + pkgNames.add(pkgName); + } + } + for (String pkgName: sLocationExtraPkgNames) { + if (pkgName != null) { + pkgNames.add(pkgName); + } + } return pkgNames; } @@ -1373,6 +1386,10 @@ public static void updateIndicatorExemptedPackages(@NonNull Context context) { for (int i = 0; i < EXEMPTED_ROLES.length; i++) { INDICATOR_EXEMPTED_PACKAGES[i] = context.getString(EXEMPTED_ROLES[i]); } + sLocationProviderPkgNames = context.getResources().getStringArray( + R.array.config_locationProviderPackageNames); + sLocationExtraPkgNames = context.getResources().getStringArray( + R.array.config_locationExtraPackageNames); } } /** diff --git a/core/java/android/permission/PermissionUsageHelper.java b/core/java/android/permission/PermissionUsageHelper.java index 11fa379f6eae3..868c28f417f81 100644 --- a/core/java/android/permission/PermissionUsageHelper.java +++ b/core/java/android/permission/PermissionUsageHelper.java @@ -56,6 +56,7 @@ import android.os.UserHandle; import android.permission.flags.Flags; import android.provider.DeviceConfig; +import android.provider.Settings; import android.telephony.TelephonyManager; import android.util.ArrayMap; import android.util.ArraySet; @@ -81,11 +82,6 @@ public class PermissionUsageHelper implements AppOpsManager.OnOpActiveChangedLis private static final String LOG_TAG = PermissionUsageHelper.class.getName(); - /** - * Whether to show the mic and camera icons. - */ - private static final String PROPERTY_CAMERA_MIC_ICONS_ENABLED = "camera_mic_icons_enabled"; - /** * How long after an access to show it as "recent" */ @@ -101,9 +97,20 @@ public class PermissionUsageHelper implements AppOpsManager.OnOpActiveChangedLis private static final long DEFAULT_RUNNING_TIME_MS = 5000L; private static final long DEFAULT_RECENT_TIME_MS = 15000L; - private static boolean shouldShowIndicators() { - return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, - PROPERTY_CAMERA_MIC_ICONS_ENABLED, true); + private boolean shouldShowIndicators() { + return shouldShowCameraIndicator() || shouldShowLocationIndicator(); + } + + private boolean shouldShowCameraIndicator() { + return Settings.Secure.getIntForUser(mContext.getContentResolver(), + Settings.Secure.ENABLE_CAMERA_PRIVACY_INDICATOR, 1, + UserHandle.USER_CURRENT) == 1; + } + + private boolean shouldShowLocationIndicator() { + return Settings.Secure.getIntForUser(mContext.getContentResolver(), + Settings.Secure.ENABLE_LOCATION_PRIVACY_INDICATOR, 1, + UserHandle.USER_CURRENT) == 1; } private static long getRecentThreshold(Long now) { @@ -116,6 +123,11 @@ private static long getRunningThreshold(Long now) { RUNNING_ACCESS_TIME_MS, DEFAULT_RUNNING_TIME_MS); } + private static final List LOCATION_OPS = List.of( + OPSTR_COARSE_LOCATION, + OPSTR_FINE_LOCATION + ); + private static final List MIC_OPS = List.of( OPSTR_PHONE_CALL_MICROPHONE, OPSTR_RECEIVE_AMBIENT_TRIGGER_AUDIO, @@ -283,9 +295,15 @@ private void addLinkToChainIfNotPresentLocked(String op, String packageName, int return usages; } - List ops = new ArrayList<>(CAMERA_OPS); - if (includeMicrophoneUsage) { - ops.addAll(MIC_OPS); + List ops = new ArrayList<>(); + if (shouldShowCameraIndicator()) { + ops.addAll(CAMERA_OPS); + if (includeMicrophoneUsage) { + ops.addAll(MIC_OPS); + } + } + if (shouldShowLocationIndicator()) { + ops.addAll(LOCATION_OPS); } Map> rawUsages = getOpUsagesByDevice(ops, deviceId); diff --git a/core/java/android/pocket/IPocketCallback.aidl b/core/java/android/pocket/IPocketCallback.aidl new file mode 100644 index 0000000000000..53e5412f89beb --- /dev/null +++ b/core/java/android/pocket/IPocketCallback.aidl @@ -0,0 +1,24 @@ +/** + * Copyright (C) 2016 The ParanoidAndroid 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.pocket; + +/** @hide */ +interface IPocketCallback { + + // notify when pocket state changes. + void onStateChanged(boolean isDeviceInPocket, int reason); + +} \ No newline at end of file diff --git a/core/java/android/pocket/IPocketService.aidl b/core/java/android/pocket/IPocketService.aidl new file mode 100644 index 0000000000000..783465774207b --- /dev/null +++ b/core/java/android/pocket/IPocketService.aidl @@ -0,0 +1,43 @@ +/** + * Copyright (C) 2016 The ParanoidAndroid 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.pocket; + +import android.pocket.IPocketCallback; + +/** @hide */ +interface IPocketService { + + // add callback to get notified about pocket state. + void addCallback(IPocketCallback callback); + + // remove callback and stop getting notified about pocket state. + void removeCallback(IPocketCallback callback); + + // notify pocket service about intercative state changed. + // @see com.android.policy.PhoneWindowManager + void onInteractiveChanged(boolean interactive); + + // external processes can request changing listening state. + void setListeningExternal(boolean listen); + + // check if device is in pocket. + boolean isDeviceInPocket(); + + // Custom methods + void setPocketLockVisible(boolean visible); + boolean isPocketLockVisible(); + +} \ No newline at end of file diff --git a/core/java/android/pocket/PocketConstants.java b/core/java/android/pocket/PocketConstants.java new file mode 100644 index 0000000000000..70aa74a7f2a69 --- /dev/null +++ b/core/java/android/pocket/PocketConstants.java @@ -0,0 +1,19 @@ +package android.pocket; + +/** + * This class contains global pocket setup constants. + * @author Carlo Savignano + * @hide + */ + +public class PocketConstants { + + public static final boolean DEBUG = false; + public static final boolean DEBUG_SPEW = false; + + /** + * Whether to use proximity sensor to evaluate pocket state. + */ + public static final boolean ENABLE_PROXIMITY_JUDGE = true; + +} diff --git a/core/java/android/pocket/PocketManager.java b/core/java/android/pocket/PocketManager.java new file mode 100644 index 0000000000000..cad5635d17ad6 --- /dev/null +++ b/core/java/android/pocket/PocketManager.java @@ -0,0 +1,237 @@ +/** + * Copyright (C) 2016 The ParanoidAndroid 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.pocket; + +import android.content.Context; +import android.os.Handler; +import android.os.PowerManager; +import android.os.RemoteException; +import android.os.SystemClock; +import android.telecom.TelecomManager; +import android.text.format.DateUtils; +import android.util.Log; +import android.util.Slog; + +/** + * A class that coordinates listening for pocket state. + *

+ * Use {@link android.content.Context#getSystemService(java.lang.String)} + * with argument {@link android.content.Context#POCKET_SERVICE} to get + * an instance of this class. + * + * Usage: import and create a final {@link IPocketCallback.Stub()} and implement your logic in + * {@link IPocketCallback#onStateChanged(boolean, int)}. Then add your callback to the pocket manager + * + * // define a final callback + * private final IPocketCallback mCallback = new IPocketCallback.Stub() { + * + * @Override + * public void onStateChanged(boolean isDeviceInPocket, int reason) { + * // Your method to handle logic outside of this callback, ideally with a handler + * // posting on UI Thread for view hierarchy operations or with its own background thread. + * handlePocketStateChanged(isDeviceInPocket, reason); + * } + * + * } + * + * // add callback to pocket manager + * private void addCallback() { + * PocketManager manager = (PocketManager) context.getSystemService(Context.POCKET_SERVICE); + * manager.addCallback(mCallback); + * } + * + * @author Carlo Savignano + * @hide + */ +public class PocketManager { + + private static final String TAG = PocketManager.class.getSimpleName(); + static final boolean DEBUG = false; + + /** + * Whether {@link IPocketCallback#onStateChanged(boolean, int)} + * was fired because of the sensor. + * @see PocketService#handleDispatchCallbacks() + */ + public static final int REASON_SENSOR = 0; + + /** + * Whether {@link IPocketCallback#onStateChanged(boolean, int)} + * was fired because of an error while accessing service. + * @see #addCallback(IPocketCallback) + * @see #removeCallback(IPocketCallback) + */ + public static final int REASON_ERROR = 1; + + /** + * Whether {@link IPocketCallback#onStateChanged(boolean, int)} + * was fired because of a needed reset. + * @see PocketService#binderDied() + */ + public static final int REASON_RESET = 2; + + private final Context mContext; + private final IPocketService mService; + private final PowerManager mPowerManager; + private final TelecomManager mTelecomManager; + private final Handler mHandler; + private boolean mPocketViewTimerActive; + + public PocketManager(Context context, IPocketService service) { + mContext = context; + mService = service; + if (mService == null) { + Slog.v(TAG, "PocketService was null"); + } + mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); + mTelecomManager = (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE); + mHandler = new Handler(); + } + + /** + * Add pocket state callback. + * @see PocketService#handleRemoveCallback(IPocketCallback) + */ + public void addCallback(final IPocketCallback callback) { + if (mService != null) try { + mService.addCallback(callback); + } catch (RemoteException e1) { + Log.w(TAG, "Remote exception in addCallback: ", e1); + if (callback != null){ + try { + callback.onStateChanged(false, REASON_ERROR); + } catch (RemoteException e2) { + Log.w(TAG, "Remote exception in callback.onPocketStateChanged: ", e2); + } + } + } + } + + /** + * Remove pocket state callback. + * @see PocketService#handleAddCallback(IPocketCallback) + */ + public void removeCallback(final IPocketCallback callback) { + if (mService != null) try { + mService.removeCallback(callback); + } catch (RemoteException e1) { + Log.w(TAG, "Remote exception in removeCallback: ", e1); + if (callback != null){ + try { + callback.onStateChanged(false, REASON_ERROR); + } catch (RemoteException e2) { + Log.w(TAG, "Remote exception in callback.onPocketStateChanged: ", e2); + } + } + } + } + + /** + * Notify service about device interactive state changed. + * {@link PhoneWindowManager#startedWakingUp()} + * {@link PhoneWindowManager#startedGoingToSleep(int)} + */ + public void onInteractiveChanged(boolean interactive) { + boolean isPocketViewShowing = (interactive && isDeviceInPocket()); + synchronized (mPocketLockTimeout) { + if (mPocketViewTimerActive != isPocketViewShowing) { + if (isPocketViewShowing) { + if (DEBUG) Log.v(TAG, "Setting pocket timer"); + mHandler.removeCallbacks(mPocketLockTimeout); // remove any pending requests + mHandler.postDelayed(mPocketLockTimeout, 3 * DateUtils.SECOND_IN_MILLIS); + mPocketViewTimerActive = true; + } else { + if (DEBUG) Log.v(TAG, "Clearing pocket timer"); + mHandler.removeCallbacks(mPocketLockTimeout); + mPocketViewTimerActive = false; + } + } + } + if (mService != null) try { + mService.onInteractiveChanged(interactive); + } catch (RemoteException e) { + Log.w(TAG, "Remote exception in addCallback: ", e); + } + } + + /** + * Request listening state change by, but not limited to, external process. + * @see PocketService#handleSetListeningExternal(boolean) + */ + public void setListeningExternal(boolean listen) { + if (mService != null) try { + mService.setListeningExternal(listen); + } catch (RemoteException e) { + Log.w(TAG, "Remote exception in setListeningExternal: ", e); + } + // Clear timeout when user hides pocket lock with long press power. + if (mPocketViewTimerActive && !listen) { + if (DEBUG) Log.v(TAG, "Clearing pocket timer due to override"); + mHandler.removeCallbacks(mPocketLockTimeout); + mPocketViewTimerActive = false; + } + } + + /** + * Return whether device is in pocket. + * @see PocketService#isDeviceInPocket() + * @return + */ + public boolean isDeviceInPocket() { + if (mService != null) try { + return mService.isDeviceInPocket(); + } catch (RemoteException e) { + Log.w(TAG, "Remote exception in isDeviceInPocket: ", e); + } + return false; + } + + class PocketLockTimeout implements Runnable { + @Override + public void run() { + if (!mTelecomManager.isInCall()) + mPowerManager.goToSleep(SystemClock.uptimeMillis()); + mPocketViewTimerActive = false; + } + } + + /** Custom methods **/ + + public void setPocketLockVisible(boolean visible) { + if (!visible){ + if (DEBUG) Log.v(TAG, "Clearing pocket timer"); + mHandler.removeCallbacks(mPocketLockTimeout); + mPocketViewTimerActive = false; + } + if (mService != null) try { + mService.setPocketLockVisible(visible); + } catch (RemoteException e) { + Log.w(TAG, "Remote exception in setPocketLockVisible: ", e); + } + } + + public boolean isPocketLockVisible() { + if (mService != null) try { + return mService.isPocketLockVisible(); + } catch (RemoteException e) { + Log.w(TAG, "Remote exception in isPocketLockVisible: ", e); + } + return false; + } + + private PocketLockTimeout mPocketLockTimeout = new PocketLockTimeout(); + +} diff --git a/core/java/android/preference/RingtonePreference.java b/core/java/android/preference/RingtonePreference.java index e15244ac0df03..3576ec87a7e7f 100644 --- a/core/java/android/preference/RingtonePreference.java +++ b/core/java/android/preference/RingtonePreference.java @@ -33,6 +33,7 @@ *

* If the user chooses the "Default" item, the saved string will be one of * {@link System#DEFAULT_RINGTONE_URI}, + * {@link System#DEFAULT_RINGTONE2_URI}, * {@link System#DEFAULT_NOTIFICATION_URI}, or * {@link System#DEFAULT_ALARM_ALERT_URI}. If the user chooses the "Silent" * item, the saved string will be an empty string. diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java index 99ff38b43adcf..035e56ec95b7e 100644 --- a/core/java/android/provider/ContactsContract.java +++ b/core/java/android/provider/ContactsContract.java @@ -3299,6 +3299,9 @@ public boolean equals(Object obj) { Bundle response = nullSafeCall(resolver, ContactsContract.AUTHORITY_URI, QUERY_DEFAULT_ACCOUNT_FOR_NEW_CONTACTS_METHOD, null, null); + if (response == null) { + return DefaultAccountAndState.ofNotSet(); + } int defaultAccountState = response.getInt(KEY_DEFAULT_ACCOUNT_STATE, -1); if (DefaultAccountAndState.isCloudOrSimAccount(defaultAccountState)) { String accountName = response.getString(Settings.ACCOUNT_NAME); diff --git a/core/java/android/provider/Downloads.java b/core/java/android/provider/Downloads.java index 0829d85801ac3..c2dbfb8925bac 100644 --- a/core/java/android/provider/Downloads.java +++ b/core/java/android/provider/Downloads.java @@ -646,6 +646,11 @@ public static boolean isStatusCompleted(int status) { */ public static final int STATUS_QUEUED_FOR_WIFI = 196; + /** + * This download is paused manually. + */ + public static final int STATUS_PAUSED_MANUAL = 197; + /** * This download couldn't be completed due to insufficient storage * space. Typically, this is because the SD card is full. diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 9e5f178f50f70..77529fc82feda 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -126,6 +126,8 @@ import java.util.concurrent.Executor; import java.util.function.Consumer; +import com.android.internal.util.crdroid.DeviceConfigUtils; + /** * The Settings provider contains global system-level device preferences. */ @@ -2993,6 +2995,21 @@ public final class Settings { public static final String ACTION_APP_PERMISSIONS_SETTINGS = "android.settings.APP_PERMISSIONS_SETTINGS"; + /** + * Activity Action: Show screen that lets user configure private DNS + *

+ * In some cases, a matching Activity may not exist, so ensure you safeguard against this. + *

+ * Input: Nothing + *

+ * Output: Nothing + * + * @hide + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_PRIVATE_DNS_SETTING = + "com.android.settings.PRIVATE_DNS_SETTINGS"; + // End of Intent actions for Settings /** @@ -5777,6 +5794,17 @@ public static void setShowGTalkServiceStatusForUser(ContentResolver cr, boolean @Readable public static final String RINGTONE = "ringtone"; + /** + * Persistent store for the system-wide default ringtone for Slot2 URI. + * + * @see #RINGTONE + * @see #DEFAULT_RINGTONE2_URI + * + */ + /** {@hide} */ + @Readable + public static final String RINGTONE2 = "ringtone2"; + /** * A {@link Uri} that will point to the current default ringtone at any * given time. @@ -5787,11 +5815,26 @@ public static void setShowGTalkServiceStatusForUser(ContentResolver cr, boolean */ public static final Uri DEFAULT_RINGTONE_URI = getUriFor(RINGTONE); + /** + * A {@link Uri} that will point to the current default ringtone for Slot2 + * at any given time. + * + * @see #DEFAULT_RINGTONE_URI + * + */ + /** {@hide} */ + public static final Uri DEFAULT_RINGTONE2_URI = getUriFor(RINGTONE2); + /** {@hide} */ public static final String RINGTONE_CACHE = "ringtone_cache"; /** {@hide} */ public static final Uri RINGTONE_CACHE_URI = getUriFor(RINGTONE_CACHE); + /** {@hide} */ + public static final String RINGTONE2_CACHE = "ringtone2_cache"; + /** {@hide} */ + public static final Uri RINGTONE2_CACHE_URI = getUriFor(RINGTONE2_CACHE); + /** * Persistent store for the system-wide default notification sound. * @@ -5995,6 +6038,19 @@ public static void setShowGTalkServiceStatusForUser(ContentResolver cr, boolean @Readable public static final String ACCELEROMETER_ROTATION = "accelerometer_rotation"; + /** + * Control the type of rotation which can be performed using the accelerometer + * if ACCELEROMETER_ROTATION is enabled. + * Value is a bitwise combination of + * 1 = 0 degrees (portrait) + * 2 = 90 degrees (left) + * 4 = 180 degrees (inverted portrait) + * 8 = 270 degrees (right) + * Setting to 0 is effectively orientation lock + * @hide + */ + public static final String ACCELEROMETER_ROTATION_ANGLES = "accelerometer_rotation_angles"; + /** * Default screen rotation when no other policy applies. * When {@link #ACCELEROMETER_ROTATION} is zero and no on-screen Activity expresses a @@ -6610,24 +6666,778 @@ public static void setShowGTalkServiceStatusForUser(ContentResolver cr, boolean * * @hide */ - public static final String CV_PREFERRED_INTENSITY = "cv_preferred_intensity"; + public static final String CV_PREFERRED_INTENSITY = "cv_preferred_intensity"; + + /** + * Integer property that specifes the color for screen flash notification as a + * packed 32-bit color. + * + * @see android.graphics.Color#argb + * @hide + */ + public static final String SCREEN_FLASH_NOTIFICATION_COLOR = + "screen_flash_notification_color_global"; + + /** + * Volume keys control cursor in text fields (default is 0) + * 0 - Disabled + * 1 - Volume up/down moves cursor left/right + * 2 - Volume up/down moves cursor right/left + * @hide + */ + @Readable + public static final String VOLUME_KEY_CURSOR_CONTROL = "volume_key_cursor_control"; + + /** + * IMPORTANT: If you add a new public settings you also have to add it to + * PUBLIC_SETTINGS below. If the new setting is hidden you have to add + * it to PRIVATE_SETTINGS below. Also add a validator that can validate + * the setting value. See an example above. + */ + + /** + * Whether the phone vibrates on call connect + * @hide + */ + @Readable + public static final String VIBRATE_ON_CONNECT = "vibrate_on_connect"; + + /** + * Whether the phone vibrates on call waiting + * @hide + */ + @Readable + public static final String VIBRATE_ON_CALLWAITING = "vibrate_on_callwaiting"; + + /** + * Whether the phone vibrates on disconnect + * @hide + */ + @Readable + public static final String VIBRATE_ON_DISCONNECT = "vibrate_on_disconnect"; + + /** + * Whether to blink flashlight for incoming calls + * 0 = Disabled (Default) + * 1 = Blink flashlight only in Ringer mode + * 2 = Blink flashlight only when ringer is not audible + * 3 = Blink flahslight only when entirely silent + * 4 = Blink flashlight always regardless of ringer mode + * @hide + */ + @Readable + public static final String FLASHLIGHT_ON_CALL = "flashlight_on_call"; + + /** + * Whether flashlight_on_call ignores DND (Zen Mode) + * @hide + */ + @Readable + public static final String FLASHLIGHT_ON_CALL_IGNORE_DND = "flashlight_on_call_ignore_dnd"; + + /** + * Rate in Hz in which to blink flashlight_on_call + * @hide + */ + @Readable + public static final String FLASHLIGHT_ON_CALL_RATE = "flashlight_on_call_rate"; + + /** + * Which Vibration Pattern to use + * 0: dzzz-dzzz + * 1: dzzz-da + * 2: mm-mm-mm + * 3: da-da-dzzz + * 4: da-dzzz-da + * 5: custom + * @hide + */ + @Readable + public static final String RINGTONE_VIBRATION_PATTERN = "ringtone_vibration_pattern"; + + /** + * Custom vibration pattern + * format: ms,ms,ms each a range from 0 to 1000 ms + * @hide + */ + @Readable + public static final String CUSTOM_RINGTONE_VIBRATION_PATTERN = "custom_ringtone_vibration_pattern"; + + /** + * Whether to show seconds next to clock in status bar + * 0 - hide (default) + * 1 - show + * @hide + */ + public static final String STATUS_BAR_CLOCK_SECONDS = "status_bar_clock_seconds"; + + /** + * Shows custom date before clock time + * 0 - No Date + * 1 - Small Date + * 2 - Normal Date + * @hide + */ + public static final String STATUS_BAR_CLOCK_DATE_DISPLAY = "status_bar_clock_date_display"; + + /** + * Sets the date string style + * 0 - Regular style + * 1 - Lowercase + * 2 - Uppercase + * @hide + */ + public static final String STATUS_BAR_CLOCK_DATE_STYLE = "status_bar_clock_date_style"; + + /** + * Position of date + * 0 - Left of clock + * 1 - Right of clock + * @hide + */ + public static final String STATUS_BAR_CLOCK_DATE_POSITION = "status_bar_clock_date_position"; + + /** + * Stores the java DateFormat string for the date + * @hide + */ + public static final String STATUS_BAR_CLOCK_DATE_FORMAT = "status_bar_clock_date_format"; + + /** + * Statusbar clock background + * 0 - hide accented chip (default) + * 1 - show accented chip + * @hide + */ + public static final String STATUSBAR_CLOCK_CHIP = "statusbar_clock_chip"; + + /** + * Double tap on lockscreen to sleep + * @hide + */ + public static final String DOUBLE_TAP_SLEEP_LOCKSCREEN = "double_tap_sleep_lockscreen"; + + /** + * Whether StatusBar icons should use app icon + * @hide + */ + public static final String STATUSBAR_COLORED_ICONS = "statusbar_colored_icons"; + + /** + * Show the pending notification counts as overlays on the status bar + * @hide + */ + public static final String STATUSBAR_NOTIF_COUNT = "statusbar_notif_count"; + + /** + * Whether to show the battery bar + * @hide + */ + public static final String STATUSBAR_BATTERY_BAR = "statusbar_battery_bar"; + + /** + * @hide + */ + public static final String STATUSBAR_BATTERY_BAR_COLOR = "statusbar_battery_bar_color"; + + /** + * @hide + */ + public static final String STATUSBAR_BATTERY_BAR_THICKNESS = + "statusbar_battery_bar_thickness"; + + /** + * @hide + */ + public static final String STATUSBAR_BATTERY_BAR_STYLE = "statusbar_battery_bar_style"; + + /** + * @hide + */ + public static final String STATUSBAR_BATTERY_BAR_ANIMATE = "statusbar_battery_bar_animate"; + + /** + * @hide + */ + public static final String STATUSBAR_BATTERY_BAR_CHARGING_COLOR = + "statusbar_battery_bar_charging_color"; + /** + * @hide + */ + public static final String STATUSBAR_BATTERY_BAR_BATTERY_LOW_COLOR = + "statusbar_battery_bar_battery_low_color"; + + /** + * @hide + */ + public static final String STATUSBAR_BATTERY_BAR_ENABLE_CHARGING_COLOR = + "statusbar_battery_bar_enable_charging_color"; + + /** + * @hide + */ + public static final String STATUSBAR_BATTERY_BAR_BLEND_COLOR = "statusbar_battery_bar_blend_color"; + + /** + * @hide + */ + public static final String STATUSBAR_BATTERY_BAR_BLEND_COLOR_REVERSE = + "statusbar_battery_bar_blend_color_reverse"; + + /** + * Enable/disable Bluetooth Battery bar + * @hide + */ + public static final String BLUETOOTH_SHOW_BATTERY = "bluetooth_show_battery"; + + /** + * Statusbar logo + * @hide + */ + public static final String STATUS_BAR_LOGO = "status_bar_logo"; + + /** + * Position of Status bar logo + * 0 - Left (default) + * 1 - Right + * @hide + */ + public static final String STATUS_BAR_LOGO_POSITION = "status_bar_logo_position"; + + /** + * Statusbar logo custom style + * @hide + */ + public static final String STATUS_BAR_LOGO_STYLE = "status_bar_logo_style"; + + /** + * Network traffic indicator + * 0 = Disabled + * 1 = Enabled + * @hide + */ + public static final String NETWORK_TRAFFIC_ENABLED = "network_traffic_enabled"; + + /** + * Network traffic indicator mode + * 0 = Display both up- and down-stream traffic + * 1 = Display up-stream traffic only + * 2 = Display down-stream traffic only + * @hide + */ + public static final String NETWORK_TRAFFIC_MODE = "network_traffic_mode"; + + /** + * Whether or not to hide the network traffic indicator when there is no activity + * @hide + */ + public static final String NETWORK_TRAFFIC_AUTOHIDE = "network_traffic_autohide"; + + /** + * Threshold below which network traffic would be hidden + * @hide + */ + public static final String NETWORK_TRAFFIC_AUTOHIDE_THRESHOLD = "network_traffic_autohide_threshold"; + + /** + * Measurement unit preference for network traffic + * @hide + */ + public static final String NETWORK_TRAFFIC_UNITS = "network_traffic_units"; + + /** + * Specify refresh duration for network traffic + * @hide + */ + public static final String NETWORK_TRAFFIC_REFRESH_INTERVAL = "network_traffic_refresh_interval"; + + /** + * Whether to hide arrows for network traffic + * @hide + */ + public static final String NETWORK_TRAFFIC_HIDEARROW = "network_traffic_hidearrow"; + + /** + * Whether to display cross sign for a data disabled connection + * @hide + */ + public static final String DATA_DISABLED_ICON = "data_disabled_icon"; + + /** + * Whether to display 4G icon instead LTE + * @hide + */ + public static final String SHOW_FOURG_ICON = "show_fourg_icon"; + + /** + * @hide + */ + public static final String WIFI_STANDARD_ICON = "wifi_standard_icon"; + + /** + * Whether to control brightness from status bar + * 0 = 0ff, 1 = on + * @hide + */ + public static final String STATUS_BAR_BRIGHTNESS_CONTROL = "status_bar_brightness_control"; + + /** + * @hide + */ + public static final String STATUSBAR_EXTRA_PADDING_START = "statusbar_extra_padding_start"; + + /** + * @hide + */ + public static final String STATUSBAR_EXTRA_PADDING_TOP = "statusbar_extra_padding_top"; + + /** + * @hide + */ + public static final String STATUSBAR_EXTRA_PADDING_END = "statusbar_extra_padding_end"; + + /** + * Whether to show Bluetooth dialog or toggle bluetooth using Bluetooth tile + * @hide + */ + public static final String QS_BT_SHOW_DIALOG = "qs_bt_show_dialog"; + + /** + * Whether to use the custom status bar header or not + * @hide + */ + public static final String STATUS_BAR_CUSTOM_HEADER = "status_bar_custom_header"; + + /** + * Whether to apply a shadow on top of the header image + * value is the alpha value of the shadow image is 0 -> no shadow -> 255 black + * @hide + */ + public static final String STATUS_BAR_CUSTOM_HEADER_SHADOW = "status_bar_custom_header_shadow"; + + /** + * header image package to use for daylight header - package name - null if default + * @hide + */ + public static final String STATUS_BAR_DAYLIGHT_HEADER_PACK = "status_bar_daylight_header_pack"; + + /** + * Current active provider - available currently "static" "daylight" + * @hide + */ + public static final String STATUS_BAR_CUSTOM_HEADER_PROVIDER = "status_bar_custom_header_provider"; + + /** + * Manual override picture to use + * @hide + */ + public static final String STATUS_BAR_CUSTOM_HEADER_IMAGE = "status_bar_custom_header_image"; + + /** + * @hide + */ + public static final String STATUS_BAR_FILE_HEADER_IMAGE = "status_bar_file_header_image"; + + /** + * Header height + * @hide + */ + public static final String STATUS_BAR_CUSTOM_HEADER_HEIGHT = "status_bar_custom_header_height"; + + /** + * @hide + */ + public static final String LOCKSCREEN_WEATHER_ENABLED = "lockscreen_weather_enabled"; + + /** + * @hide + */ + public static final String LOCKSCREEN_WEATHER_LOCATION = "lockscreen_weather_location"; + + /** + * @hide + */ + public static final String LOCKSCREEN_WEATHER_TEXT = "lockscreen_weather_text"; + + /** + * @hide + */ + public static final String LOCKSCREEN_WEATHER_WIND_INFO = "lockscreen_weather_wind_info"; + + /** + * @hide + */ + public static final String LOCKSCREEN_WEATHER_HUMIDITY_INFO = "lockscreen_weather_humidity_info"; + + /** + * Whether to show the battery info on the lockscreen while charging + * @hide + */ + public static final String LOCKSCREEN_BATTERY_INFO = "lockscreen_battery_info"; + + /** + * Whether to enable the ripple animation on fingerprint unlock + * @hide + */ + public static final String ENABLE_RIPPLE_EFFECT = "enable_ripple_effect"; + + /** + * Whether to show power menu on LockScreen + * @hide + */ + public static final String LOCKSCREEN_ENABLE_POWER_MENU = "lockscreen_enable_power_menu"; + + /** + * @hide + */ + public static final String UDFPS_ANIM_STYLE = "udfps_anim_style"; + + /** + * @hide + */ + public static final String UDFPS_ICON = "udfps_icon"; + + /** + * Whether to vibrate on succesful fingerprint authentication + * @hide + */ + public static final String FP_SUCCESS_VIBRATE = "fp_success_vibrate"; + + /** + * Whether to vibrate on unsuccesful fingerprint authentication + * @hide + */ + public static final String FP_ERROR_VIBRATE = "fp_error_vibrate"; + + /** + * Whether to show the carrier name on the lockscreen + * @hide + */ + public static final String LOCKSCREEN_SHOW_CARRIER = "lockscreen_show_carrier"; + + /** + * Disable hw buttons + * @hide + */ + public static final String HARDWARE_KEYS_DISABLE = "hardware_keys_disable"; + + /** + * Swap capacitive keys + * @hide + */ + public static final String SWAP_CAPACITIVE_KEYS = "swap_capacitive_keys"; + + /** + * Indicates whether ANBI (Accidental navigation button interaction) is enabled. + * @hide + */ + public static final String ANBI_ENABLED = "anbi_enabled"; + + /** + * If On-The-Go should be displayed at the power menu. + * @hide + */ + public static final String GLOBAL_ACTIONS_ONTHEGO = "global_actions_onthego"; + + /** + * The alpha value of the On-The-Go overlay. + * @hide + */ + public static final String ON_THE_GO_ALPHA = "on_the_go_alpha"; + + /** + * Whether the service should restart itself or not. + * @hide + */ + public static final String ON_THE_GO_SERVICE_RESTART = "on_the_go_service_restart"; + + /** + * The camera instance to use. + * 0 = Rear Camera + * 1 = Front Camera + * @hide + */ + public static final String ON_THE_GO_CAMERA = "on_the_go_camera"; + + /** + * Whether to show or hide alert slider notifications on supported devices + * @hide + */ + public static final String ALERT_SLIDER_NOTIFICATIONS = "alert_slider_notifications"; + + /** + * Whether to show daily/weekly data usage in the QS footer. + * @hide + */ + public static final String QS_SHOW_DATA_USAGE = "qs_show_data_usage"; + + /** + * Persist setting for showing either daily or weekly data usage in the QS footer. + * @hide + */ + public static final String QS_SHOW_DATA_USAGE_WINDOW = "qs_show_data_usage_window"; + + /** + * Haptic feedback on brightness slider + * @hide + */ + public static final String QS_BRIGHTNESS_SLIDER_HAPTIC = "qs_brightness_slider_haptic"; + + /** + * Customize QS tile shape. + * @hide + */ + public static final String QS_TILE_SHAPE = "qs_tile_shape"; + + /** + * Customize Brightness slider shape. + * @hide + */ + public static final String QS_BRIGHTNESS_SLIDER_SHAPE = "qs_brightness_slider_shape"; + + /** + * Haptic feedback on QS tiles + * @hide + */ + public static final String QS_TILE_HAPTIC = "qs_tile_haptic"; + + /** + * @hide + */ + public static final String QS_TILES_COLUMNS_LANDSCAPE = "qs_tiles_columns_landscape"; + + /** + * @hide + */ + public static final String QS_TILES_COLUMNS = "qs_tiles_columns"; + + /** + * @hide + */ + public static final String QS_TILES_ROWS_LANDSCAPE = "qs_tiles_rows_landscape"; + + /** + * @hide + */ + public static final String QS_TILES_ROWS = "qs_tiles_rows"; + + /** + * @hide + */ + public static final String QQS_TILES_ROWS_LANDSCAPE = "qqs_tiles_rows_landscape"; + + /** + * @hide + */ + public static final String QQS_TILES_ROWS = "qqs_tiles_rows"; + + /** + * Lockscreen Media Art + * @hide + */ + public static final String LS_MEDIA_ART_ENABLED = "ls_media_art_enabled"; + + /** + * @hide + */ + public static final String AMBIENT_MEDIA_ART_ENABLED = "ambient_media_art_enabled"; + + /** + * @hide + */ + public static final String LS_MEDIA_ART_FILTER = "ls_media_art_filter"; + + /** + * @hide + */ + public static final String LS_MEDIA_ART_FADE_LEVEL = "ls_media_art_fade_level"; + + /** + * @hide + */ + public static final String LS_MEDIA_ART_BLUR_LEVEL = "ls_media_art_blur_level"; + + /** + * Whether edge light is enabled + * @hide + */ + public static final String EDGE_LIGHT_ENABLED = "edge_light_enabled"; + + /** + * Color mode of edge light + * @hide + */ + public static final String EDGE_LIGHT_COLOR_MODE = "edge_light_color_mode"; + + /** + * Custom color (hex value) for edge light + * @hide + */ + public static final String EDGE_LIGHT_CUSTOM_COLOR = "edge_light_custom_color"; + + /** + * Pulse count for edge light + * @hide + */ + public static final String EDGE_LIGHT_PULSE_COUNT = "edge_light_pulse_count"; + + /** + * Stroke width for edge light + * @hide + */ + public static final String EDGE_LIGHT_STROKE_WIDTH = "edge_light_stroke_width"; + + /** + * Edge light style + * @hide + */ + public static final String EDGE_LIGHT_STYLE = "edge_light_style"; + + /** + * Edge light animation effect type + * @hide + */ + public static final String EDGE_LIGHT_ANIMATION_EFFECT = "edge_light_animation_effect"; + + /** + * Gesture navbar length mode. + * Supported modes: 0 for short length, 1 for normal and 2 for long. + * @hide + */ + public static final String GESTURE_NAVBAR_LENGTH_MODE = "gesture_navbar_length_mode"; + + /** @hide */ + public static final String BACK_GESTURE_HEIGHT = "back_gesture_height"; + + /** + * @hide + */ + public static final String GESTURE_NAVBAR_HEIGHT_MODE = "gesture_navbar_height_mode"; + + /** + * GameSpace: List of added games by user + * @hide + */ + @Readable + public static final String GAMESPACE_GAME_LIST = "gamespace_game_list"; + + /** + * GameSpace: Whether fullscreen intent will be suppressed while in game session + * @hide + */ + @Readable + public static final String GAMESPACE_SUPPRESS_FULLSCREEN_INTENT = "gamespace_suppress_fullscreen_intent"; + + /** + * Current status of whether gestures are locked + * @hide + */ + public static final String LOCK_GESTURE_STATUS = "lock_gesture_status"; + + /** + * Whether allowing pocket service to register sensors and dispatch informations. + * 0 = disabled + * 1 = enabled + * @hide + */ + public static final String POCKET_JUDGE = "pocket_judge"; + + /** + * Whether to show heads up only for dialer and sms apps + * @hide + */ + public static final String LESS_BORING_HEADS_UP = "less_boring_heads_up"; + + /** + * Whether to play notification sound and vibration if screen is ON + * 0 - never + * 1 - always + * @hide + */ + public static final String NOTIFICATION_SOUND_VIB_SCREEN_ON = "notification_sound_vib_screen_on"; + + /** + * Heads up timeout configuration + * @hide + */ + public static final String HEADS_UP_TIMEOUT = "heads_up_timeout"; + + /** + * Whether to show the kill app button in notification guts + * @hide + */ + public static final String NOTIFICATION_GUTS_KILL_APP_BUTTON = + "notification_guts_kill_app_button"; + + /** + * Whether to show charging animation + * @hide + */ + public static final String CHARGING_ANIMATION = "charging_animation"; + + /** + * Force full screen for devices with cutout + * @hide + */ + public static final String FORCE_FULLSCREEN_CUTOUT_APPS = "force_full_screen_cutout_apps"; + + /** + * Whether to enable Smart Pixels + * @hide + */ + public static final String SMART_PIXELS_ENABLE = "smart_pixels_enable"; + + /** + * Smart Pixels pattern + * @hide + */ + public static final String SMART_PIXELS_PATTERN = "smart_pixels_pattern"; + + /** + * Smart Pixels Shift Timeout + * @hide + */ + public static final String SMART_PIXELS_SHIFT_TIMEOUT = "smart_pixels_shift_timeout"; + + /** + * Whether Smart Pixels should enable on power saver mode + * @hide + */ + public static final String SMART_PIXELS_ON_POWER_SAVE = "smart_pixels_on_power_save"; + + /** + * Defines the screen-off animation to display + * @hide + */ + public static final String SCREEN_OFF_ANIMATION = "screen_off_animation"; + + /** + * Whether to show rotation suggestion + * @hide + */ + @Readable + public static final String ENABLE_ROTATION_BUTTON = "enable_rotation_button"; + + /** + * Adaptive playback + * Automatically pause media when the volume is muted and + * will resume automatically when volume is restored. + * 0 = disabled + * 1 = enabled + * @hide + */ + public static final String ADAPTIVE_PLAYBACK_ENABLED = "adaptive_playback_enabled"; /** - * Integer property that specifes the color for screen flash notification as a - * packed 32-bit color. - * - * @see android.graphics.Color#argb + * Adaptive playback's timeout in ms * @hide */ - public static final String SCREEN_FLASH_NOTIFICATION_COLOR = - "screen_flash_notification_color_global"; + public static final String ADAPTIVE_PLAYBACK_TIMEOUT = "adaptive_playback_timeout"; /** - * IMPORTANT: If you add a new public settings you also have to add it to - * PUBLIC_SETTINGS below. If the new setting is hidden you have to add - * it to PRIVATE_SETTINGS below. Also add a validator that can validate - * the setting value. See an example above. + * @hide */ + public static final String SCREENSHOT_SHUTTER_SOUND = "screenshot_shutter_sound"; /** * Keys we no longer back up under the current schema, but want to continue to @@ -6677,6 +7487,7 @@ public static void setShowGTalkServiceStatusForUser(ContentResolver cr, boolean PUBLIC_SETTINGS.add(VOLUME_BLUETOOTH_SCO); PUBLIC_SETTINGS.add(VOLUME_ASSISTANT); PUBLIC_SETTINGS.add(RINGTONE); + PUBLIC_SETTINGS.add(RINGTONE2); PUBLIC_SETTINGS.add(NOTIFICATION_SOUND); PUBLIC_SETTINGS.add(ALARM_ALERT); PUBLIC_SETTINGS.add(TEXT_AUTO_REPLACE); @@ -6804,6 +7615,7 @@ public static void getCloneToManagedProfileSettings(Set outKeySet) { public static final Map CLONE_FROM_PARENT_ON_VALUE = new ArrayMap<>(); static { CLONE_FROM_PARENT_ON_VALUE.put(RINGTONE, Secure.SYNC_PARENT_SOUNDS); + CLONE_FROM_PARENT_ON_VALUE.put(RINGTONE2, Secure.SYNC_PARENT_SOUNDS); CLONE_FROM_PARENT_ON_VALUE.put(NOTIFICATION_SOUND, Secure.SYNC_PARENT_SOUNDS); CLONE_FROM_PARENT_ON_VALUE.put(ALARM_ALERT, Secure.SYNC_PARENT_SOUNDS); } @@ -7158,10 +7970,13 @@ public static final class Secure extends NameValueTable { @UnsupportedAppUsage private static final HashSet MOVED_TO_GLOBAL; static { - MOVED_TO_LOCK_SETTINGS = new HashSet<>(3); + MOVED_TO_LOCK_SETTINGS = new HashSet<>(6); MOVED_TO_LOCK_SETTINGS.add(Secure.LOCK_PATTERN_ENABLED); MOVED_TO_LOCK_SETTINGS.add(Secure.LOCK_PATTERN_VISIBLE); MOVED_TO_LOCK_SETTINGS.add(Secure.LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED); + MOVED_TO_LOCK_SETTINGS.add(Secure.LOCK_PATTERN_SIZE); + MOVED_TO_LOCK_SETTINGS.add(Secure.LOCK_DOTS_VISIBLE); + MOVED_TO_LOCK_SETTINGS.add(Secure.LOCK_SHOW_ERROR_PATH); MOVED_TO_GLOBAL = new HashSet<>(); MOVED_TO_GLOBAL.add(Settings.Global.ADB_ENABLED); @@ -8696,6 +9511,24 @@ public static boolean putFloatForUser(ContentResolver cr, String name, float val public static final String LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED = "lock_pattern_tactile_feedback_enabled"; + /** + * Determines the width and height of the LockPatternView widget + * @hide + */ + public static final String LOCK_PATTERN_SIZE = "lock_pattern_size"; + + /** + * Whether lock pattern will show dots (0 = false, 1 = true) + * @hide + */ + public static final String LOCK_DOTS_VISIBLE = "lock_pattern_dotsvisible"; + + /** + * Whether lockscreen error pattern is visible (0 = false, 1 = true) + * @hide + */ + public static final String LOCK_SHOW_ERROR_PATH = "lock_pattern_show_error_path"; + /** * This preference allows the device to be locked given time after screen goes off, * subject to current DeviceAdmin policy limits. @@ -10513,6 +11346,28 @@ public static boolean putFloatForUser(ContentResolver cr, String name, float val @Readable public static final String DOZE_ALWAYS_ON = "doze_always_on"; + /** + * Indicates whether doze turns on automatically + * 0 = disabled (default) + * 1 = from sunset to sunrise + * 2 = custom time + * 3 = from sunset till a time + * 4 = from a time till sunrise + * @hide + */ + @Readable + public static final String DOZE_ALWAYS_ON_AUTO_MODE = "doze_always_on_auto_mode"; + + /** + * The custom time {@link DOZE_ALWAYS_ON} should be on at + * Only relevant when {@link DOZE_ALWAYS_ON_AUTO_MODE} is set to 2 and above + * 0 = Disabled (default) + * format: HH:mm,HH:mm (since,till) + * @hide + */ + @Readable + public static final String DOZE_ALWAYS_ON_AUTO_TIME = "doze_always_on_auto_time"; + /** * Indicates whether ambient wallpaper is visible with AOD. *

@@ -10520,6 +11375,7 @@ public static boolean putFloatForUser(ContentResolver cr, String name, float val * * @hide */ + @Readable public static final String DOZE_ALWAYS_ON_WALLPAPER_ENABLED = "doze_always_on_wallpaper_enabled"; @@ -10528,7 +11384,7 @@ public static boolean putFloatForUser(ContentResolver cr, String name, float val * @hide */ @Readable - public static final String DOZE_PICK_UP_GESTURE = "doze_pulse_on_pick_up"; + public static final String DOZE_PICK_UP_GESTURE = "doze_pick_up_gesture"; /** * Whether the device should pulse on long press gesture. @@ -10580,6 +11436,36 @@ public static boolean putFloatForUser(ContentResolver cr, String name, float val @Readable public static final String SUPPRESS_DOZE = "suppress_doze"; + /** + * Pulse notifications on tilt + * @hide + */ + public static final String DOZE_TILT_GESTURE = "doze_tilt_gesture"; + + /** + * Pulse notifications on hand wave + * @hide + */ + public static final String DOZE_HANDWAVE_GESTURE = "doze_handwave_gesture"; + + /** + * Pulse notifications on removal from pocket + * @hide + */ + public static final String DOZE_POCKET_GESTURE = "doze_pocket_gesture"; + + /** + * Wake up instead of pulsing notifications + * @hide + */ + public static final String RAISE_TO_WAKE_GESTURE = "raise_to_wake_gesture"; + + /** + * Vibrate when pulsing notifications on gesture + * @hide + */ + public static final String DOZE_GESTURE_VIBRATE = "doze_gesture_vibrate"; + /** * Gesture that skips media. * @hide @@ -12494,9 +13380,15 @@ public static boolean putFloatForUser(ContentResolver cr, String name, float val */ public static final String VOLUME_DIALOG_DISMISS_TIMEOUT = "volume_dialog_dismiss_timeout"; + /** + * Volume dialog haptic feedback + * @hide + */ + public static final String VOLUME_DIALOG_HAPTIC_FEEDBACK = "volume_dialog_haptic_feedback"; + /** * What behavior should be invoked when the volume hush gesture is triggered - * One of VOLUME_HUSH_OFF, VOLUME_HUSH_VIBRATE, VOLUME_HUSH_MUTE. + * One of VOLUME_HUSH_OFF, VOLUME_HUSH_VIBRATE, VOLUME_HUSH_MUTE, VOLUME_HUSH_CYCLE. * * @hide */ @@ -12513,6 +13405,8 @@ public static boolean putFloatForUser(ContentResolver cr, String name, float val /** @hide */ @SystemApi public static final int VOLUME_HUSH_MUTE = 2; + /** @hide */ + public static final int VOLUME_HUSH_CYCLE = 3; /** * The number of times (integer) the user has manually enabled battery saver. @@ -13187,6 +14081,229 @@ public static boolean putFloatForUser(ContentResolver cr, String name, float val public static final String HBM_SETTING_KEY = "com.android.server.display.HBM_SETTING_KEY"; + /** + * User selectable keybox data. + * @hide + */ + @Readable + public static final String KEYBOX_DATA = "keybox_data"; + + /** + * Store vboot key. + * @hide + */ + @Readable + public static final String VBOOT_KEY = "vboot_key"; + + /** + * Store vboot hash. + * @hide + */ + @Readable + public static final String VBOOT_HASH = "vboot_hash"; + + /** + * Whether to use PIF spoof for google apps + * @hide + */ + @Readable + public static final String PI_ENABLE_SPOOF = "pi_enable_spoof"; + + /** + * Whether to disable GMS cert chain with custom keybox + * @hide + */ + @Readable + public static final String PI_GMS_CERT_CHAIN = "pi_gms_cert_chain"; + + /** + * Whether to use PIF spoof for games + * @hide + */ + @Readable + public static final String PI_GAMES_SPOOF = "pi_games_spoof"; + + /** + * Whether to use PIF spoof for photos + * @hide + */ + @Readable + public static final String PI_PHOTOS_SPOOF = "pi_photos_spoof"; + + /** + * Whether to use PIF spoof for netflix + * @hide + */ + @Readable + public static final String PI_NETFLIX_SPOOF = "pi_netflix_spoof"; + + /** + * Whether to show privacy indicator for location + * @hide + */ + public static final String ENABLE_LOCATION_PRIVACY_INDICATOR = "enable_location_privacy_indicator"; + + /** + * Whether to show privacy indicator for camera + * @hide + */ + public static final String ENABLE_CAMERA_PRIVACY_INDICATOR = "enable_camera_privacy_indicator"; + + /** + * Whether to show privacy indicator for media projection + * @hide + */ + public static final String ENABLE_PROJECTION_PRIVACY_INDICATOR = "enable_projection_privacy_indicator"; + + /** + * Whether to allow swipe down on lockscreen to view Quick Panel + * @hide + */ + public static final String ENABLE_LOCKSCREEN_QUICK_SETTINGS = "enable_lockscreen_quick_settings"; + + /** + * Pulse lockscreen music visualizer + * @hide + */ + public static final String LOCKSCREEN_PULSE_ENABLED = "lockscreen_pulse_enabled"; + + /** + * Pulse lockscreen music visualizer on ambient display + * @hide + */ + public static final String AMBIENT_PULSE_ENABLED = "ambient_pulse_enabled"; + + /** + * @hide + */ + public static final String PULSE_BAR_COUNT = "pulse_bar_count"; + + /** + * @hide + */ + public static final String PULSE_ROUNDED_BARS = "pulse_rounded_bars"; + + /** + * @hide + */ + public static final String PULSE_COLOR = "pulse_color"; + + /** + * @hide + */ + public static final String PULSE_RENDERER = "pulse_renderer"; + + /** + * Inverse navigation bar layout + * @hide + */ + public static final String NAVBAR_INVERSE_LAYOUT = "navbar_inverse_layout"; + + /** + * Whether to show or hide the arrow for back gesture + * @hide + */ + public static final String BACK_GESTURE_ARROW = "back_gesture_arrow"; + + /** + * Whether or not to vibrate when back gesture is used + * @hide + */ + public static final String BACK_GESTURE_HAPTIC = "back_gesture_haptic"; + + /** + * Which navigation bar layout to use + * 0 = Normal (Default) + * 1 = Compact + * 2 = Left-leaning + * 3 = Right-leaning + * @hide + */ + public static final String NAVBAR_LAYOUT_MODE = "navbar_layout_mode"; + + /** + * Show navigation space below IME + * @hide + */ + public static final String NAVBAR_IME_SPACE = "navbar_ime_space"; + + /** + * Our GameSpace can't write to device_config directly [GTS] + * Use this as intermediate to pass device_config property + * from our GameSpace to com.android.server.app.GameManagerService + * so we can set the device_config property from there. + * @hide + */ + public static final String GAME_OVERLAY = "game_overlay"; + + /** + * Whether to show an overlay in the bottom corner of the screen on copying stuff + * into the clipboard. + * @hide + */ + public static final String SHOW_CLIPBOARD_OVERLAY = "show_clipboard_overlay"; + + /** + * Whether to enable DOZE only when charging + * @hide + */ + public static final String DOZE_ON_CHARGE = "doze_on_charge"; + + /** + * Whether to pulse ambient on new music tracks + * @hide + */ + public static final String PULSE_ON_NEW_TRACKS = "pulse_on_new_tracks"; + + /** + * Whether to show media squiggle animation + * @hide + */ + public static final String MEDIA_SQUIGGLE_ANIMATION = "media_squiggle_animation"; + + /** + * Control whether the process CPU info meter should be shown. + * @hide + */ + public static final String SHOW_CPU_OVERLAY = "show_cpu_overlay"; + + /** + * Control whether the process FPS info meter should be shown. + * @hide + */ + public static final String SHOW_FPS_OVERLAY = "show_fps_overlay"; + + /** + * Whether to enable clipboard auto clear + * @hide + */ + public static final String CLIPBOARD_AUTO_CLEAR_ENABLED = "clipboard_auto_clear_enabled"; + + /** + * Timeout length for clipboard auto clear + * @hide + */ + public static final String CLIPBOARD_AUTO_CLEAR_TIMEOUT = "clipboard_auto_clear_timeout"; + + /** + * Whether to turn off Private DNS {@link #PRIVATE_DNS_MODE} + * when a VPN is connected + *

+ * Set to 1 for true and 0 for false. Default 0. + * + * @hide + */ + public static final String VPN_ENFORCE_DNS = "vpn_enforce_dns"; + + /** + * A setting used to store the last mode of {@link #PRIVATE_DNS_MODE} + * used for {@link #VPN_ENFORCE_DNS} + * Not for backup! + * + * @hide + */ + public static final String VPN_ENFORCE_DNS_STORE = "vpn_enforce_dns_store"; + /** * Keys we no longer back up under the current schema, but want to continue to * process when restoring historical backup datasets. @@ -13446,6 +14563,17 @@ public static boolean putFloatForUser(ContentResolver cr, String name, float val public static final String EXTRA_AUTOMATIC_POWER_SAVE_MODE = "extra_automatic_power_save_mode"; + /** + * Whether tethering is allowed to use VPN upstreams. (0 = false, 1 = true) + * Any changes here must also be reflected in: + * packages/modules/Connectivity/Tethering/src/com/android/networkstack/tethering/ + * Tethering.java + * packages/modules/Connectivity/Tethering/src/com/android/networkstack/tethering/ + * UpstreamNetworkMonitor.java + * @hide + */ + public static final String TETHERING_ALLOW_VPN_UPSTREAMS = "tethering_allow_vpn_upstreams"; + /** * Whether contextual screen timeout is enabled. * @@ -13463,11 +14591,10 @@ public static boolean putFloatForUser(ContentResolver cr, String name, float val "hinge_angle_lidevent_enabled"; /** - * Whether lockscreen weather is enabled. - * + * Whether lockscreen smartspace is enabled. * @hide */ - public static final String LOCK_SCREEN_WEATHER_ENABLED = "lockscreen_weather_enabled"; + public static final String LOCKSCREEN_SMARTSPACE_ENABLED = "lockscreen_smartspace_enabled"; /** * Whether the feature that the device will fire a haptic when users scroll and hit @@ -13690,6 +14817,7 @@ public static void setLocationProviderEnabled(ContentResolver cr, * * @hide */ + @Readable public static final String CHARGE_OPTIMIZATION_MODE = "charge_optimization_mode"; /** @@ -17349,6 +18477,30 @@ public static final class Global extends NameValueTable { public static final String PREFERRED_NETWORK_MODE = "preferred_network_mode"; + /** + * Force LTE Carrier Aggregation setting per SIM slot. + * Use FORCE_LTE_CA_0 for SIM slot 0 (first SIM) + * Use FORCE_LTE_CA_1 for SIM slot 1 (second SIM) + * + * Type: int (0 = disabled, 1 = enabled) + * @hide + */ + public static final String FORCE_LTE_CA = "force_lte_ca"; + + /** + * Force LTE Carrier Aggregation for SIM slot 0 + * Type: int (0 = disabled, 1 = enabled) + * @hide + */ + public static final String FORCE_LTE_CA_0 = "force_lte_ca_0"; + + /** + * Force LTE Carrier Aggregation for SIM slot 1 + * Type: int (0 = disabled, 1 = enabled) + * @hide + */ + public static final String FORCE_LTE_CA_1 = "force_lte_ca_1"; + /** * Name of an application package to be debugged. */ @@ -18920,6 +20072,14 @@ public static final class Global extends NameValueTable { */ public static final String ONE_HANDED_KEYGUARD_SIDE = "one_handed_keyguard_side"; + /** + * A list of uids that are allowed to use restricted networks. + * + * @hide + */ + public static final String UIDS_ALLOWED_ON_RESTRICTED_NETWORKS = + "uids_allowed_on_restricted_networks"; + /** * A semi-colon separated list of Bluetooth hearing devices' local ambient volume data. * Each entry is encoded as a key=value list, separated by commas. Ex: @@ -18967,6 +20127,32 @@ public static final class Global extends NameValueTable { CLOCKWORK_HOME_READY, }; + /** + * The amount of time in milliseconds before wifi is turned off + * @hide + */ + public static final String WIFI_OFF_TIMEOUT = "wifi_off_timeout"; + + /** + * The amount of time in milliseconds before bluetooth is turned off + * @hide + */ + public static final String BLUETOOTH_OFF_TIMEOUT = "bluetooth_off_timeout"; + + /** + * Sensor block per-package + * @hide + */ + @Readable + public static final String SENSOR_BLOCK = "sensor_block"; + + /** + * Sensor blocked packages + * @hide + */ + @Readable + public static final String SENSOR_BLOCKED_APP = "sensor_blocked_app"; + /** * Keys we no longer back up under the current schema, but want to continue to * process when restoring historical backup datasets. @@ -20176,6 +21362,12 @@ public static boolean putFloat(ContentResolver cr, String name, float value) { */ public static final String RESTRICTED_NETWORKING_MODE = "restricted_networking_mode"; + /** + * Control whether application downgrade is allowed. + * @hide + */ + public static final String PM_DOWNGRADE_ALLOWED = "pm_downgrade_allowed"; + /** * Setting indicating whether Low Power Standby is enabled, if supported. * @@ -21572,6 +22764,9 @@ public static Map getStrings(@NonNull ContentResolver resolver, @RequiresPermission(Manifest.permission.WRITE_DEVICE_CONFIG) public static boolean putString(@NonNull String namespace, @NonNull String name, @Nullable String value, boolean makeDefault) { + if (DeviceConfigUtils.shouldDenyDeviceConfigControl(namespace, name)) { + return true; + } ContentResolver resolver = getContentResolver(); return sNameValueCache.putStringForUser(resolver, createCompositeName(namespace, name), value, null, makeDefault, resolver.getUserId(), @@ -21593,7 +22788,9 @@ public static boolean putString(@NonNull String namespace, public static boolean setStrings(@NonNull String namespace, @NonNull Map keyValues) throws DeviceConfig.BadConfigException { - return setStrings(getContentResolver(), namespace, keyValues); + boolean result = setStrings(getContentResolver(), namespace, keyValues); + DeviceConfigUtils.setDefaultProperties(namespace, null); + return result; } /** @@ -21643,6 +22840,9 @@ public static boolean setStrings(@NonNull ContentResolver resolver, @RequiresPermission(Manifest.permission.WRITE_DEVICE_CONFIG) public static boolean deleteString(@NonNull String namespace, @NonNull String name) { + if (DeviceConfigUtils.shouldDenyDeviceConfigControl(namespace, name)) { + return true; + } ContentResolver resolver = getContentResolver(); return sNameValueCache.deleteStringForUser(resolver, createCompositeName(namespace, name), resolver.getUserId()); @@ -21684,6 +22884,7 @@ public static void resetToDefaults(@ResetMode int resetMode, } catch (RemoteException e) { Log.w(TAG, "Can't reset to defaults for " + CONTENT_URI, e); } + DeviceConfigUtils.setDefaultProperties(null, null); } /** diff --git a/core/java/android/security/authenticationpolicy/AuthenticationPolicyManager.java b/core/java/android/security/authenticationpolicy/AuthenticationPolicyManager.java index a5defe383445f..4b0d302a10075 100644 --- a/core/java/android/security/authenticationpolicy/AuthenticationPolicyManager.java +++ b/core/java/android/security/authenticationpolicy/AuthenticationPolicyManager.java @@ -100,28 +100,29 @@ public final class AuthenticationPolicyManager { mSecureLockDeviceStatusListeners = new ConcurrentHashMap<>(); - /** - * Success result code for {@link #enableSecureLockDevice} and {@link #disableSecureLockDevice}. + * Error result code for {@link #enableSecureLockDevice} and {@link #disableSecureLockDevice}. * - * Secure lock device request successful. + * Secure lock device request status unknown. * * @hide */ @SystemApi @FlaggedApi(FLAG_SECURE_LOCKDOWN) - public static final int SUCCESS = 0; + public static final int ERROR_UNKNOWN = 0; /** - * Error result code for {@link #enableSecureLockDevice} and {@link #disableSecureLockDevice}. + * Success result code for {@link #enableSecureLockDevice} and {@link #disableSecureLockDevice}. * - * Secure lock device request status unknown. + * Secure lock device request successful. * * @hide */ @SystemApi @FlaggedApi(FLAG_SECURE_LOCKDOWN) - public static final int ERROR_UNKNOWN = 1; + public static final int SUCCESS = 1; + + /** * Error result code for {@link #enableSecureLockDevice} and {@link #disableSecureLockDevice}. @@ -197,8 +198,8 @@ public final class AuthenticationPolicyManager { * @hide */ @IntDef(prefix = {"ENABLE_SECURE_LOCK_DEVICE_STATUS_"}, value = { - SUCCESS, ERROR_UNKNOWN, + SUCCESS, ERROR_UNSUPPORTED, ERROR_INVALID_PARAMS, ERROR_NO_BIOMETRICS_ENROLLED, @@ -214,8 +215,8 @@ public final class AuthenticationPolicyManager { * @hide */ @IntDef(prefix = {"DISABLE_SECURE_LOCK_DEVICE_STATUS_"}, value = { - SUCCESS, ERROR_UNKNOWN, + SUCCESS, ERROR_UNSUPPORTED, ERROR_INVALID_PARAMS, ERROR_NOT_AUTHORIZED diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java index 845c08437a75c..c9a778f3fccae 100644 --- a/core/java/android/service/dreams/DreamService.java +++ b/core/java/android/service/dreams/DreamService.java @@ -1146,6 +1146,27 @@ public void setDozeScreenBrightness(int brightness) { } } + /** + * Sets the screen brightness. + * + * This method sets the underlying {@link LayoutParams#screenBrightness} property. Since this + * affects the window, {@link #setScreenBrightness(float)} should not be used for windowless + * dreams. + * + * @hide For use by SystemUI components only. + */ + @UnsupportedAppUsage + public void setScreenBrightness(float brightness) { + if (mWindow == null) { + Slog.e(TAG, "trying to set screen brightness without window"); + return; + } + + WindowManager.LayoutParams lp = mWindow.getAttributes(); + lp.screenBrightness = brightness; + mWindow.setAttributes(lp); + } + /** * Sets the screen brightness to use while dozing. *

diff --git a/core/java/android/service/notification/StatusBarNotification.java b/core/java/android/service/notification/StatusBarNotification.java index 79957f4115973..949959861dec2 100644 --- a/core/java/android/service/notification/StatusBarNotification.java +++ b/core/java/android/service/notification/StatusBarNotification.java @@ -86,10 +86,13 @@ public class StatusBarNotification implements Parcelable { private final Map mContextForDisplayId = Collections.synchronizedMap(new ArrayMap<>()); + private boolean mIsContentSecure; + /** @hide */ public StatusBarNotification(String pkg, String opPkg, int id, String tag, int uid, int initialPid, Notification notification, UserHandle user, - String overrideGroupKey, long postTime) { + String overrideGroupKey, long postTime, + boolean isContentSecure) { if (pkg == null) throw new NullPointerException(); if (notification == null) throw new NullPointerException(); @@ -105,6 +108,7 @@ public StatusBarNotification(String pkg, String opPkg, int id, this.overrideGroupKey = overrideGroupKey; this.key = key(); this.groupKey = groupKey(); + mIsContentSecure = isContentSecure; } /** @@ -152,6 +156,7 @@ public StatusBarNotification(Parcel in) { } this.key = key(); this.groupKey = groupKey(); + mIsContentSecure = in.readBoolean(); } /** @@ -256,6 +261,7 @@ public void writeToParcel(Parcel out, int flags) { } else { out.writeInt(0); } + out.writeBoolean(mIsContentSecure); } public int describeContents() { @@ -297,7 +303,8 @@ public StatusBarNotification clone() { public StatusBarNotification cloneShallow(Notification notification) { StatusBarNotification result = new StatusBarNotification(this.pkg, this.opPkg, this.id, this.tag, this.uid, this.initialPid, - notification, this.user, this.overrideGroupKey, this.postTime); + notification, this.user, this.overrideGroupKey, + this.postTime, mIsContentSecure); result.setInstanceId(this.mInstanceId); return result; } @@ -606,4 +613,24 @@ private String shortenTag(String logTag) { return logTag.substring(0, MAX_LOG_TAG_LENGTH - hash.length() - 1) + "-" + hash; } + + /** + * Set whether the notification content is secure. + * + * @param isContentSecure whether the content is secure. + * @hide + */ + public void setIsContentSecure(boolean isContentSecure) { + mIsContentSecure = isContentSecure; + } + + /** + * Check whether the notification content is secure. + * + * @return true if content is secure, false otherwise. + * @hide + */ + public boolean getIsContentSecure() { + return mIsContentSecure; + } } diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java index 9554d257730aa..98cabc7accac7 100644 --- a/core/java/android/service/notification/ZenModeConfig.java +++ b/core/java/android/service/notification/ZenModeConfig.java @@ -566,7 +566,7 @@ private static boolean sameCondition(ZenRule rule) { } private static int[] generateMinuteBuckets() { - final int maxHrs = 12; + final int maxHrs = 24; final int[] buckets = new int[maxHrs + 3]; buckets[0] = 15; buckets[1] = 30; diff --git a/core/java/android/speech/tts/TtsEngines.java b/core/java/android/speech/tts/TtsEngines.java index a8aea7c1eb599..3859418de2174 100644 --- a/core/java/android/speech/tts/TtsEngines.java +++ b/core/java/android/speech/tts/TtsEngines.java @@ -498,7 +498,7 @@ static public String[] toOldLocaleStringFormat(Locale locale) { * specific preference in the list. */ private static String parseEnginePrefFromList(String prefValue, String engineName) { - if (TextUtils.isEmpty(prefValue)) { + if (TextUtils.isEmpty(prefValue) || TextUtils.isEmpty(engineName)) { return null; } @@ -507,7 +507,7 @@ private static String parseEnginePrefFromList(String prefValue, String engineNam for (String value : prefValues) { final int delimiter = value.indexOf(':'); if (delimiter > 0) { - if (engineName.equals(value.substring(0, delimiter))) { + if ((value.substring(0, delimiter)).equals(engineName)) { return value.substring(delimiter + 1); } } @@ -560,7 +560,7 @@ private String updateValueInCommaSeparatedList(String list, String key, for (String value : prefValues) { final int delimiter = value.indexOf(':'); if (delimiter > 0) { - if (key.equals(value.substring(0, delimiter))) { + if (value.substring(0, delimiter).equals(key)) { if (first) { first = false; } else { diff --git a/core/java/android/text/method/MetaKeyKeyListener.java b/core/java/android/text/method/MetaKeyKeyListener.java index 7c9c2f1327931..ecc74fda533a4 100644 --- a/core/java/android/text/method/MetaKeyKeyListener.java +++ b/core/java/android/text/method/MetaKeyKeyListener.java @@ -349,8 +349,6 @@ private void press(Editable content, Object what) { if (state == PRESSED) ; // repeat before use - else if (state == RELEASED) - content.setSpan(what, 0, 0, LOCKED); else if (state == USED) ; // repeat after use else if (state == LOCKED) diff --git a/core/java/android/util/DisplayUtils.java b/core/java/android/util/DisplayUtils.java index cbb38a4ada31a..91562c5d3f5d9 100644 --- a/core/java/android/util/DisplayUtils.java +++ b/core/java/android/util/DisplayUtils.java @@ -16,8 +16,10 @@ package android.util; +import android.content.Context; import android.content.res.Resources; import android.view.Display; +import android.view.DisplayInfo; import com.android.internal.R; @@ -83,4 +85,20 @@ public static float getPhysicalPixelDisplaySizeRatio( final float heightRatio = (float) currentHeight / physicalHeight; return Math.min(widthRatio, heightRatio); } + + /** + * Get the display size ratio for the current resolution vs the maximum supported + * resolution. + */ + public static float getScaleFactor(Context context) { + DisplayInfo displayInfo = new DisplayInfo(); + context.getDisplay().getDisplayInfo(displayInfo); + final Display.Mode maxDisplayMode = + getMaximumResolutionDisplayMode(displayInfo.supportedModes); + final float scaleFactor = getPhysicalPixelDisplaySizeRatio( + maxDisplayMode.getPhysicalWidth(), maxDisplayMode.getPhysicalHeight(), + displayInfo.getNaturalWidth(), displayInfo.getNaturalHeight()); + + return scaleFactor; + } } diff --git a/core/java/android/util/TimingsTraceLog.java b/core/java/android/util/TimingsTraceLog.java index b4f4729c82b27..9c3a02a5d3ead 100644 --- a/core/java/android/util/TimingsTraceLog.java +++ b/core/java/android/util/TimingsTraceLog.java @@ -37,7 +37,7 @@ @android.ravenwood.annotation.RavenwoodKeepWholeClass public class TimingsTraceLog { // Debug boot time for every step if it's non-user build. - private static final boolean DEBUG_BOOT_TIME = !Build.IS_USER; + private static final boolean DEBUG_BOOT_TIME = false; // Maximum number of nested calls that are stored private static final int MAX_NESTED_CALLS = 10; diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java index 3a2ec91b3b207..02860a9ea0007 100644 --- a/core/java/android/view/Choreographer.java +++ b/core/java/android/view/Choreographer.java @@ -1019,7 +1019,9 @@ void doFrame(long frameTimeNanos, int frame, // Calculating jitter involves using the original frame time without // adjustments from buffer stuffing final long jitterNanos = startNanos - frameTimeNanos; - if (jitterNanos >= frameIntervalNanos) { + if (jitterNanos >= frameIntervalNanos + && (timeline.mVsyncId != FrameInfo.INVALID_VSYNC_ID + || vsyncEventData.frameInterval != -1)) { frameTimeNanos = startNanos; if (frameIntervalNanos == 0) { Log.i(TAG, "Vsync data empty due to timeout"); diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index 000a857edf0cb..8a6caaa7f4547 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -390,7 +390,7 @@ interface IWindowManager /** * Sets display rotation to {@link rotation} if auto-rotate is OFF. */ - void setRotationAtAngleIfLocked(int rotation, String caller); + void setRotationAtAngleIfAllowed(int rotation, String caller); /** * Lock the display orientation to the specified rotation, or to the current diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java index 25eb9be3c5d9a..57384b47207a0 100644 --- a/core/java/android/view/InputDevice.java +++ b/core/java/android/view/InputDevice.java @@ -354,6 +354,14 @@ public final class InputDevice implements Parcelable { */ public static final int SOURCE_SENSOR = 0x04000000 | SOURCE_CLASS_NONE; + /** + * The input source is a specific virtual event sent from navigation bar. + * + * @see com.android.systemui.navigationbar.buttons.KeyButtonView#sendEvent() + * @hide + */ + public static final int SOURCE_NAVIGATION_BAR = 0x06000000 | SOURCE_CLASS_BUTTON; + /** * A special input source constant that is used when filtering input devices * to match devices that provide any type of input source. diff --git a/core/java/android/view/InputEventReceiver.java b/core/java/android/view/InputEventReceiver.java index fbe983368b2e1..d90542c5ebbed 100644 --- a/core/java/android/view/InputEventReceiver.java +++ b/core/java/android/view/InputEventReceiver.java @@ -227,7 +227,13 @@ public final void finishInputEvent(InputEvent event, boolean handled) { } else { int seq = mSeqMap.valueAt(index); mSeqMap.removeAt(index); - nativeFinishInputEvent(mReceiverPtr, seq, handled); + try { + nativeFinishInputEvent(mReceiverPtr, seq, handled); + } catch (RuntimeException e) { + // Just log the exception instead of crashing + Log.w(TAG, "Exception in nativeFinishInputEvent: " + e.getMessage()); + return; + } } } event.recycleIfNeededAfterDispatch(); diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java index c6d73268561e5..34a94baacccd6 100644 --- a/core/java/android/view/KeyEvent.java +++ b/core/java/android/view/KeyEvent.java @@ -2151,6 +2151,12 @@ public class KeyEvent extends InputEvent implements Parcelable { */ public static final int FLAG_FALLBACK = 0x400; + /** + * Flag that indicates that event was sent from EdgeBackGestureHandler. + * @hide + */ + public static final int FLAG_LONG_SWIPE = 0x800; + /** * This flag indicates that this event was modified by or generated from an accessibility * service. Value = 0x800 @@ -2937,6 +2943,7 @@ public static final boolean isSystemKey(int keyCode) { public static final boolean isWakeKey(int keyCode) { switch (keyCode) { case KeyEvent.KEYCODE_CAMERA: + case KeyEvent.KEYCODE_FOCUS: case KeyEvent.KEYCODE_MENU: case KeyEvent.KEYCODE_PAIRING: case KeyEvent.KEYCODE_STEM_1: @@ -2944,6 +2951,9 @@ public static final boolean isWakeKey(int keyCode) { case KeyEvent.KEYCODE_STEM_3: case KeyEvent.KEYCODE_WAKEUP: case KeyEvent.KEYCODE_STEM_PRIMARY: + case KeyEvent.KEYCODE_VOLUME_UP: + case KeyEvent.KEYCODE_VOLUME_DOWN: + case KeyEvent.KEYCODE_VOLUME_MUTE: return true; } return false; diff --git a/core/java/android/view/LayoutInflater.java b/core/java/android/view/LayoutInflater.java index 901fc02f0040f..a58680ea408e9 100644 --- a/core/java/android/view/LayoutInflater.java +++ b/core/java/android/view/LayoutInflater.java @@ -555,7 +555,7 @@ public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean atta root); } // Create layout params that match root, if supplied - params = root.generateLayoutParams(attrs); + params = root.generateLayoutParams(inflaterContext, attrs); if (!attachToRoot) { // Set the layout params for temp if we are not // attaching. (If we are, we use addView, below) @@ -689,67 +689,80 @@ public final View createView(@NonNull Context viewContext, @NonNull String name, throws ClassNotFoundException, InflateException { Objects.requireNonNull(viewContext); Objects.requireNonNull(name); - Constructor constructor = sConstructorMap.get(name); - if (constructor != null && !verifyClassLoader(constructor)) { - constructor = null; - sConstructorMap.remove(name); - } + String prefixedName = prefix != null ? (prefix + name) : name; Class clazz = null; try { Trace.traceBegin(Trace.TRACE_TAG_VIEW, name); - if (constructor == null) { - // Class not found in the cache, see if it's real, and try to add it - clazz = Class.forName(prefix != null ? (prefix + name) : name, false, - mContext.getClassLoader()).asSubclass(View.class); - - if (mFilter != null && clazz != null) { - boolean allowed = mFilter.onLoadClass(clazz); - if (!allowed) { - failNotAllowed(name, prefix, viewContext, attrs); - } + // Opportunistically create view directly instead of using reflection + View view; + try { + view = tryCreateViewDirect(prefixedName, viewContext, attrs); + } catch (Exception e) { + view = null; + } + if (view == null) { + Constructor constructor = sConstructorMap.get(name); + if (constructor != null && !verifyClassLoader(constructor)) { + constructor = null; + sConstructorMap.remove(name); } - constructor = clazz.getConstructor(mConstructorSignature); - constructor.setAccessible(true); - sConstructorMap.put(name, constructor); - } else { - // If we have a filter, apply it to cached constructor - if (mFilter != null) { - // Have we seen this name before? - Boolean allowedState = mFilterMap.get(name); - if (allowedState == null) { - // New class -- remember whether it is allowed - clazz = Class.forName(prefix != null ? (prefix + name) : name, false, - mContext.getClassLoader()).asSubclass(View.class); - - boolean allowed = clazz != null && mFilter.onLoadClass(clazz); - mFilterMap.put(name, allowed); + + if (constructor == null) { + // Class not found in the cache, see if it's real, and try to add it + clazz = Class.forName(prefixedName, false, + mContext.getClassLoader()).asSubclass(View.class); + + if (mFilter != null && clazz != null) { + boolean allowed = mFilter.onLoadClass(clazz); if (!allowed) { failNotAllowed(name, prefix, viewContext, attrs); } - } else if (allowedState.equals(Boolean.FALSE)) { - failNotAllowed(name, prefix, viewContext, attrs); + } + constructor = clazz.getConstructor(mConstructorSignature); + constructor.setAccessible(true); + sConstructorMap.put(name, constructor); + } else { + // If we have a filter, apply it to cached constructor + if (mFilter != null) { + // Have we seen this name before? + Boolean allowedState = mFilterMap.get(name); + if (allowedState == null) { + // New class -- remember whether it is allowed + clazz = Class.forName(prefixedName, false, + mContext.getClassLoader()).asSubclass(View.class); + + boolean allowed = clazz != null && mFilter.onLoadClass(clazz); + mFilterMap.put(name, allowed); + if (!allowed) { + failNotAllowed(name, prefix, viewContext, attrs); + } + } else if (allowedState.equals(Boolean.FALSE)) { + failNotAllowed(name, prefix, viewContext, attrs); + } } } - } - Object lastContext = mConstructorArgs[0]; - mConstructorArgs[0] = viewContext; - Object[] args = mConstructorArgs; - args[1] = attrs; + Object lastContext = mConstructorArgs[0]; + mConstructorArgs[0] = viewContext; + Object[] args = mConstructorArgs; + args[1] = attrs; - try { - final View view = constructor.newInstance(args); - if (view instanceof ViewStub) { - // Use the same context when inflating ViewStub later. - final ViewStub viewStub = (ViewStub) view; - viewStub.setLayoutInflater(cloneInContext((Context) args[0])); + try { + view = constructor.newInstance(args); + } finally { + mConstructorArgs[0] = lastContext; } - return view; - } finally { - mConstructorArgs[0] = lastContext; } + + if (view instanceof ViewStub) { + // Use the same context when inflating ViewStub later. + final ViewStub viewStub = (ViewStub) view; + viewStub.setLayoutInflater(cloneInContext((Context) viewContext)); + } + + return view; } catch (NoSuchMethodException e) { final InflateException ie = new InflateException( getParserStateDescription(viewContext, attrs) @@ -1011,7 +1024,8 @@ void rInflate(XmlPullParser parser, View parent, Context context, } else { final View view = createViewFromTag(parent, name, context, attrs); final ViewGroup viewGroup = (ViewGroup) parent; - final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs); + final ViewGroup.LayoutParams params = + viewGroup.generateLayoutParams(context, attrs); rInflateChildren(parser, view, attrs, true); viewGroup.addView(view, params); } @@ -1134,12 +1148,12 @@ private void parseInclude(XmlPullParser parser, Context context, View parent, // tag, false means we need to rely on the included layout params. ViewGroup.LayoutParams params = null; try { - params = group.generateLayoutParams(attrs); + params = group.generateLayoutParams(context, attrs); } catch (RuntimeException e) { // Ignore, just fail over to child attrs. } if (params == null) { - params = group.generateLayoutParams(childAttrs); + params = group.generateLayoutParams(context, childAttrs); } view.setLayoutParams(params); @@ -1243,4 +1257,62 @@ protected void dispatchDraw(Canvas canvas) { } } } + + // Some of the views included here are deprecated, but apps still use them. + @SuppressWarnings("deprecation") + private static View tryCreateViewDirect(String name, Context context, AttributeSet attributeSet) { + // This contains all the framework views used in a set of 113 real-world apps, sorted by + // number of occurrences. While views with only 1 occurrence are unlikely to be worth + // optimizing, it doesn't hurt to include them because switch-case is compiled into a table + // lookup after calling String#hashCode(). + switch (name) { + case "android.widget.LinearLayout": // 13486 occurrences + return new android.widget.LinearLayout(context, attributeSet); + case "android.widget.View": // 6930 occurrences + case "android.webkit.View": // 63 occurrences + case "android.view.View": // 63 occurrences + case "android.app.View": // 62 occurrences + return new android.view.View(context, attributeSet); + case "android.widget.FrameLayout": // 6447 occurrences + return new android.widget.FrameLayout(context, attributeSet); + case "android.widget.ViewStub": // 5613 occurrences + case "android.view.ViewStub": // 228 occurrences + case "android.app.ViewStub": // 227 occurrences + case "android.webkit.ViewStub": // 226 occurrences + return new android.view.ViewStub(context, attributeSet); + case "android.widget.TextView": // 4722 occurrences + return new android.widget.TextView(context, attributeSet); + case "android.widget.ImageView": // 3044 occurrences + return new android.widget.ImageView(context, attributeSet); + case "android.widget.RelativeLayout": // 2665 occurrences + return new android.widget.RelativeLayout(context, attributeSet); + case "android.widget.Space": // 1694 occurrences + return new android.widget.Space(context, attributeSet); + case "android.widget.ProgressBar": // 770 occurrences + return new android.widget.ProgressBar(context, attributeSet); + case "android.widget.Button": // 382 occurrences + return new android.widget.Button(context, attributeSet); + case "android.widget.ImageButton": // 265 occurrences + return new android.widget.ImageButton(context, attributeSet); + case "android.widget.Switch": // 145 occurrences + return new android.widget.Switch(context, attributeSet); + case "android.widget.DateTimeView": // 117 occurrences + return new android.widget.DateTimeView(context, attributeSet); + case "android.widget.Toolbar": // 86 occurrences + return new android.widget.Toolbar(context, attributeSet); + case "android.widget.HorizontalScrollView": // 68 occurrences + return new android.widget.HorizontalScrollView(context, attributeSet); + case "android.widget.ScrollView": // 67 occurrences + return new android.widget.ScrollView(context, attributeSet); + case "android.widget.NotificationHeaderView": // 65 occurrences + case "android.webkit.NotificationHeaderView": // 65 occurrences + case "android.view.NotificationHeaderView": // 65 occurrences + case "android.app.NotificationHeaderView": // 65 occurrences + return new android.view.NotificationHeaderView(context, attributeSet); + case "android.widget.ListView": // 58 occurrences + return new android.widget.ListView(context, attributeSet); + } + + return null; + } } diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 1c6bd4268ca29..4dde26ebc11e3 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -23133,6 +23133,11 @@ protected void onDetachedFromWindowInternal() { cleanupDraw(); mCurrentAnimation = null; + if (mScrollCache != null && mScrollCache.state == ScrollabilityCache.ON + && mAttachInfo != null) { + mAttachInfo.mHandler.removeCallbacks(mScrollCache); + } + if ((mViewFlags & TOOLTIP) == TOOLTIP) { removeCallbacks(mTooltipInfo.mShowTooltipRunnable); removeCallbacks(mTooltipInfo.mHideTooltipRunnable); diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java index 0d01a2b602946..35c01ddb6d9b6 100644 --- a/core/java/android/view/ViewConfiguration.java +++ b/core/java/android/view/ViewConfiguration.java @@ -274,7 +274,7 @@ public class ViewConfiguration { * The coefficient of friction applied to flings/scrolls. */ @UnsupportedAppUsage - private static final float SCROLL_FRICTION = 0.015f; + private static final float SCROLL_FRICTION = 0.009f; /** * Max distance in dips to overscroll for edge effects diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 8f6a8a14b28af..23a66e97a990d 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -2923,7 +2923,7 @@ private void resetTouchState() { * Returns true if the flag was previously set. */ private static boolean resetCancelNextUpFlag(@NonNull View view) { - if ((view.mPrivateFlags & PFLAG_CANCEL_NEXT_UP_EVENT) != 0) { + if (view != null && (view.mPrivateFlags & PFLAG_CANCEL_NEXT_UP_EVENT) != 0) { view.mPrivateFlags &= ~PFLAG_CANCEL_NEXT_UP_EVENT; return true; } @@ -4324,6 +4324,12 @@ protected void dispatchDraw(@NonNull Canvas canvas) { final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder); final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex); + if (child == null) { + Log.e(View.VIEW_LOG_TAG, "dispatchDraw: child view is null" + + " curIndex=" + i + ", pkg =" + mContext.getPackageName() + + " mChildrenCount=" + mChildrenCount); + return; + } if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) { more |= drawChild(canvas, child, drawingTime); } @@ -6848,6 +6854,12 @@ public LayoutParams generateLayoutParams(AttributeSet attrs) { return new LayoutParams(getContext(), attrs); } + /** @hide */ + public LayoutParams generateLayoutParams(Context inflationContext, AttributeSet attrs) { + // Call the previous method for backwards compatibility + return generateLayoutParams(attrs); + } + /** * Returns a safe set of layout parameters based on the supplied layout params. * When a ViewGroup is passed a View whose layout params do not pass the test of diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 2e76a6cd64b21..484a7a1a288a4 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -106,8 +106,14 @@ import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; +import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG; +import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG; +import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; +import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL; +import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL; import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT; +import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG; import static android.view.WindowManager.LayoutParams.TYPE_TOAST; import static android.view.WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY; import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_SANDBOXING_VIEW_BOUNDS_APIS; @@ -313,6 +319,7 @@ import java.util.Objects; import java.util.OptionalInt; import java.util.Queue; +import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicBoolean; @@ -352,6 +359,19 @@ public final class ViewRootImpl implements ViewParent, private static final int LOGTAG_INPUT_FOCUS = 62001; private static final int LOGTAG_VIEWROOT_DRAW_EVENT = 60004; + private static final Set NO_VOTE_WINDOW_TYPES = Set.of( + TYPE_INPUT_METHOD, + TYPE_INPUT_METHOD_DIALOG, + TYPE_KEYGUARD_DIALOG, + TYPE_NAVIGATION_BAR, + TYPE_NAVIGATION_BAR_PANEL, + TYPE_STATUS_BAR, + TYPE_SYSTEM_ALERT, + TYPE_SYSTEM_DIALOG, + TYPE_TOAST, + TYPE_VOLUME_OVERLAY + ); + /** * This change disables the {@code DRAW_WAKE_LOCK}, an internal wakelock acquired per-frame * duration display DOZE. It was added to allow animation during AOD. This wakelock consumes @@ -921,7 +941,7 @@ default void onConfigurationChanged(@NonNull Configuration overrideConfig, int mLastTouchPointerId; /** Tracks last {@link MotionEvent#getToolType(int)} with {@link MotionEvent#ACTION_UP}. **/ private int mLastClickToolType; - + private CharSequence mLastBackKeyDownTitle; private boolean mProfileRendering; private Choreographer.FrameCallback mRenderProfiler; private boolean mRenderProfilingEnabled; @@ -3496,9 +3516,6 @@ private static boolean shouldUseDisplaySize(final WindowManager.LayoutParams lp) * TODO(b/260382739): Apply this to all windows. */ private static boolean shouldOptimizeMeasure(final WindowManager.LayoutParams lp) { - if (com.android.window.flags.Flags.reduceUnnecessaryMeasure()) { - return true; - } return (lp.privateFlags & PRIVATE_FLAG_OPTIMIZE_MEASURE) != 0; } @@ -3515,6 +3532,10 @@ int dipToPx(int dip) { return (int) (displayMetrics.density * dip + 0.5f); } + private boolean isNoVoteWindowType() { + return NO_VOTE_WINDOW_TYPES.contains(mWindowAttributes.type); + } + private void performTraversals() { mLastPerformTraversalsSkipDrawReason = null; @@ -3863,9 +3884,7 @@ private void performTraversals() { if (surfaceControlChanged && mDisplayDecorationCached) { updateDisplayDecoration(); } - if (surfaceControlChanged - && mWindowAttributes.type - == WindowManager.LayoutParams.TYPE_STATUS_BAR) { + if (surfaceControlChanged && isNoVoteWindowType()) { mTransaction.setDefaultFrameRateCompatibility(mSurfaceControl, Surface.FRAME_RATE_COMPATIBILITY_NO_VOTE).apply(); } @@ -6609,6 +6628,7 @@ void dispatchDetachedFromWindow() { mView.assignParent(null); mView = null; mAttachInfo.mRootView = null; + mFallbackEventHandler.setView(null); destroySurface(); @@ -7754,12 +7774,14 @@ private int doOnBackKeyEvent(KeyEvent keyEvent) { } } else if (topCallback != null) { if (keyEvent.getAction() == KeyEvent.ACTION_UP) { - if (!keyEvent.isCanceled()) { + if (!keyEvent.isCanceled() && getTitle()!= null && getTitle().equals(mLastBackKeyDownTitle)) { dispatcher.tryInvokeSystemNavigationObserverCallback(); topCallback.onBackInvoked(); } else { Log.d(mTag, "Skip onBackInvoked(), reason: keyEvent.isCanceled=true"); } + } else { + mLastBackKeyDownTitle = getTitle(); } } // Do not cancel the keyEvent if no callback can handle the back event. @@ -7911,6 +7933,11 @@ private int processMotionEvent(QueuedInputEvent q) { private int processPointerEvent(QueuedInputEvent q) { final MotionEvent event = (MotionEvent)q.mEvent; + if (event.getPointerCount() == 3 && isThreeFingersSwipeActive()) { + event.setAction(MotionEvent.ACTION_CANCEL); + Log.d(mTag, "canceling motionEvent because of threeGesture detecting"); + } + // Translate the pointer event for compatibility, if needed. if (mTranslator != null) { mTranslator.translateEventInScreenToAppWindow(event); @@ -8393,7 +8420,6 @@ private boolean updatePointerIcon(MotionEvent event) { } if (x < 0 || x >= mView.getWidth() || y < 0 || y >= mView.getHeight()) { // E.g. when moving window divider with mouse - Slog.d(mTag, "updatePointerIcon called with position out of bounds"); return false; } @@ -13715,4 +13741,13 @@ private void preInitBufferAllocator() { public Choreographer getChoreographer() { return mChoreographer; } + + private boolean isThreeFingersSwipeActive() { + try { + return ActivityManager.getService().isThreeFingersSwipeActive(); + } catch (RemoteException e) { + Log.e(mTag, "isThreeFingersSwipeActive exception", e); + return false; + } + } } diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 0f2f68e6c01bf..7d319835b02c4 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -908,6 +908,12 @@ public interface KeyboardShortcutsReceiver { */ int TAKE_SCREENSHOT_FULLSCREEN = 1; + /** + * Invoke screenshot flow allowing the user to select a region. + * @hide + */ + int TAKE_SCREENSHOT_SELECTED_REGION = 2; + /** * Invoke screenshot flow with an image provided by the caller. * @hide @@ -920,6 +926,7 @@ public interface KeyboardShortcutsReceiver { * @hide */ @IntDef({TAKE_SCREENSHOT_FULLSCREEN, + TAKE_SCREENSHOT_SELECTED_REGION, TAKE_SCREENSHOT_PROVIDED_IMAGE}) @interface ScreenshotType {} diff --git a/core/java/android/view/animation/AnimationUtils.java b/core/java/android/view/animation/AnimationUtils.java index 8a407c3252f0c..f2d49ffc7d253 100644 --- a/core/java/android/view/animation/AnimationUtils.java +++ b/core/java/android/view/animation/AnimationUtils.java @@ -103,9 +103,7 @@ public static void lockAnimationClock(long vsyncMillis, long expectedPresentatio AnimationState state = sAnimationState.get(); state.animationClockLocked = true; state.currentVsyncTimeMillis = vsyncMillis; - if (!sExpectedPresentationTimeFlagValue) { - state.mExpectedPresentationTimeNanos = expectedPresentationTimeNanos; - } + state.mExpectedPresentationTimeNanos = expectedPresentationTimeNanos; } /** @@ -150,7 +148,9 @@ public static void lockAnimationClock(long vsyncMillis) { */ @TestApi public static void unlockAnimationClock() { - sAnimationState.get().animationClockLocked = false; + AnimationState state = sAnimationState.get(); + state.animationClockLocked = false; + state.mExpectedPresentationTimeNanos = 0L; } /** @@ -185,11 +185,13 @@ public static long currentAnimationTimeMillis() { @FlaggedApi(FLAG_EXPECTED_PRESENTATION_TIME_READ_ONLY) public static long getExpectedPresentationTimeNanos() { if (!sExpectedPresentationTimeFlagValue) { - return SystemClock.uptimeMillis() * TimeUtils.NANOS_PER_MS; + return System.nanoTime(); } AnimationState state = sAnimationState.get(); - return state.mExpectedPresentationTimeNanos; + return (state.mExpectedPresentationTimeNanos != 0L) + ? state.mExpectedPresentationTimeNanos + : System.nanoTime(); } /** diff --git a/core/java/android/view/inputmethod/InputMethodInfo.java b/core/java/android/view/inputmethod/InputMethodInfo.java index 8fef2d726b2cd..31c7b7b20ef7b 100644 --- a/core/java/android/view/inputmethod/InputMethodInfo.java +++ b/core/java/android/view/inputmethod/InputMethodInfo.java @@ -40,6 +40,7 @@ import android.graphics.drawable.Drawable; import android.icu.util.ULocale; import android.inputmethodservice.InputMethodService; +import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; @@ -53,6 +54,7 @@ import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; +import java.io.InputStream; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -114,6 +116,24 @@ public final class InputMethodInfo implements Parcelable { static final String TAG = "InputMethodInfo"; + /** + * The maximum allowed size in bytes for an input method's metadata XML file + * (typically {@code inputmethod.xml} referenced by + * {@link android.inputmethodservice.InputMethod#SERVICE_META_DATA}). + * This limit is enforced to prevent {@link OutOfMemoryError OutOfMemoryErrors} + * when the Android system parses input method manifests. + *

+ * An excessively large metadata file, whether due to misconfiguration or + * malicious intent, could consume an unreasonable amount of memory in the + * system process, potentially leading to instability or denial of service. + *

+ *

+ * The current recommended value is 200 KB (200 * 1024 bytes), which is + * significantly larger than typical input method metadata files. + *

+ */ + private static final int MAX_METADATA_SIZE_BYTES = 200 * 1024; // 200 KB + /** * The Service that implements this input method component. */ @@ -273,14 +293,14 @@ public InputMethodInfo(Context context, ResolveInfo service, XmlResourceParser parser = null; final ArrayList subtypes = new ArrayList(); try { + Resources res = pm.getResourcesForApplication(si.applicationInfo); + validateXmlMetaData(si, res); parser = si.loadXmlMetaData(pm, InputMethod.SERVICE_META_DATA); if (parser == null) { throw new XmlPullParserException("No " + InputMethod.SERVICE_META_DATA + " meta-data"); } - Resources res = pm.getResourcesForApplication(si.applicationInfo); - AttributeSet attrs = Xml.asAttributeSet(parser); int type; @@ -428,6 +448,55 @@ public InputMethodInfo(Context context, ResolveInfo service, mIsVirtualDeviceOnly = isVirtualDeviceOnly; } + /** + * Validates the XML metadata for an input method service to prevent OOM errors + * from excessively large XML files. + * + * @param si The ServiceInfo of the input method. + * @param res The input method app resources. + * @throws IOException if there's an issue reading the resource. + * @throws NameNotFoundException if the application's resources cannot be found. + * @throws XmlPullParserException if the metadata file exceeds the size limit. + */ + private static void validateXmlMetaData(@NonNull ServiceInfo si, @NonNull Resources res) + throws IOException, NameNotFoundException, XmlPullParserException { + final Bundle metaData = si.metaData; + if (metaData == null) { + // No metadata, skip validation. + return; + } + final int resourceId = metaData.getInt(InputMethod.SERVICE_META_DATA); + if (resourceId == 0) { + // No metadata, skip validation. + return; + } + + // Validate file size using InputStream.skip() + long totalBytesSkipped = 0; + // Loop to ensure we skip the required number of bytes, as a single + // call to skip() is not guaranteed to skip the full amount. + try (InputStream is = res.openRawResource(resourceId)) { + while (totalBytesSkipped < MAX_METADATA_SIZE_BYTES) { + long bytesSkipped = is.skip(MAX_METADATA_SIZE_BYTES - totalBytesSkipped); + if (bytesSkipped <= 0) { + // end of the stream. + break; + } + totalBytesSkipped += bytesSkipped; + } + + // If we successfully skipped exactly MAX_METADATA_SIZE_BYTES + if (totalBytesSkipped == MAX_METADATA_SIZE_BYTES) { + // try to read one more byte. + if (is.read() != -1) { + throw new XmlPullParserException( + "Input method metadata exceeds maximum allowed limit of 200KB for " + + si.packageName + ". InputMethod will not be loaded. "); + } + } + } + } + /** * @hide */ diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index a5cc414fa2c68..ebd5b136304b9 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -3336,6 +3336,7 @@ private boolean startInputInner(@StartInputReason int startInputReason, @SoftInputModeFlags int softInputMode, @WindowManager.LayoutParams.Flags int windowFlags) { final View view; + final ViewRootImpl viewRoot; synchronized (mH) { view = getServedViewLocked(); @@ -3374,13 +3375,14 @@ private boolean startInputInner(@StartInputReason int startInputReason, if (windowGainingFocus == null) { windowGainingFocus = view.getWindowToken(); - if (windowGainingFocus == null) { + viewRoot = view.getViewRootImpl(); + if (windowGainingFocus == null && viewRoot == null) { Log.e(TAG, "ABORT input: ServedView must be attached to a Window"); return false; } startInputFlags = getStartInputFlags(view, startInputFlags); - softInputMode = view.getViewRootImpl().mWindowAttributes.softInputMode; - windowFlags = view.getViewRootImpl().mWindowAttributes.flags; + softInputMode = viewRoot.mWindowAttributes.softInputMode; + windowFlags = viewRoot.mWindowAttributes.flags; } // Okay we are now ready to call into the served view and have it diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index dcacebd7feb72..5bf25d4c6a837 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -699,6 +699,7 @@ public abstract class AbsListView extends AdapterView implements Te private int mMinimumVelocity; @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 124051740) private int mMaximumVelocity; + private int mDecacheThreshold; private float mVelocityScale = 1.0f; final boolean[] mIsScrap = new boolean[1]; @@ -1015,6 +1016,7 @@ private void initAbsListView() { mVerticalScrollFactor = configuration.getScaledVerticalScrollFactor(); mMinimumVelocity = configuration.getScaledMinimumFlingVelocity(); mMaximumVelocity = configuration.getScaledMaximumFlingVelocity(); + mDecacheThreshold = mMaximumVelocity / 2; mOverscrollDistance = configuration.getScaledOverscrollDistance(); mOverflingDistance = configuration.getScaledOverflingDistance(); @@ -4255,6 +4257,10 @@ private void onTouchUp(MotionEvent ev) { } mSelector.setHotspot(x, ev.getY()); } + if (!mDataChanged && !mIsDetaching + && isAttachedToWindow()) { + performClick.run(); + } if (mTouchModeReset != null) { removeCallbacks(mTouchModeReset); } @@ -4265,10 +4271,6 @@ public void run() { mTouchMode = TOUCH_MODE_REST; child.setPressed(false); setPressed(false); - if (!mDataChanged && !mIsDetaching - && isAttachedToWindow()) { - performClick.run(); - } } }; postDelayed(mTouchModeReset, @@ -4969,7 +4971,7 @@ public void run() { // Keep the fling alive a little longer postDelayed(this, FLYWHEEL_TIMEOUT); } else { - endFling(); + endFling(false); // Don't disable the scrolling cache right after it was enabled mTouchMode = TOUCH_MODE_SCROLL; reportScrollStateChange(OnScrollListener.SCROLL_STATE_TOUCH_SCROLL); } @@ -4989,6 +4991,11 @@ float getSplineFlingDistance(int velocity) { // Use AbsListView#fling(int) instead @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) void start(int initialVelocity) { + if (Math.abs(initialVelocity) > mDecacheThreshold) { + // For long flings, scrolling cache causes stutter, so don't use it + clearScrollingCache(); + } + int initialY = initialVelocity < 0 ? Integer.MAX_VALUE : 0; mLastFlingY = initialY; mScroller.setInterpolator(null); @@ -5069,6 +5076,10 @@ void startScroll(int distance, int duration, boolean linear, // To interrupt a fling early you should use smoothScrollBy(0,0) instead @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) void endFling() { + endFling(true); + } + + void endFling(boolean clearCache) { mTouchMode = TOUCH_MODE_REST; removeCallbacks(this); @@ -5077,7 +5088,8 @@ void endFling() { if (!mSuppressIdleStateChangeCall) { reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE); } - clearScrollingCache(); + if (clearCache) + clearScrollingCache(); mScroller.abortAnimation(); if (mFlingStrictSpan != null) { diff --git a/core/java/android/widget/AbsSeekBar.java b/core/java/android/widget/AbsSeekBar.java index 6281ee9d05d10..11b02eb68dedb 100644 --- a/core/java/android/widget/AbsSeekBar.java +++ b/core/java/android/widget/AbsSeekBar.java @@ -1013,7 +1013,14 @@ private void trackTouchEvent(MotionEvent event) { progress += scale * range + getMin(); setHotspot(x, y); - setProgressInternal(Math.round(progress), true, false); + setProgressInternal(updateTouchProgress(getProgress(), Math.round(progress)), true, false); + } + + /** + * @hide + */ + protected int updateTouchProgress(int lastProgress, int newProgress) { + return newProgress; } /** diff --git a/core/java/android/widget/DateTimeView.java b/core/java/android/widget/DateTimeView.java index ca9cd66188aad..8eaebde4a40ea 100644 --- a/core/java/android/widget/DateTimeView.java +++ b/core/java/android/widget/DateTimeView.java @@ -165,10 +165,6 @@ void update() { if (mLocalTime == null || getVisibility() == GONE) { return; } - if (mShowRelativeTime) { - updateRelativeTime(); - return; - } int display; ZoneId zoneId = ZoneId.systemDefault(); @@ -476,6 +472,13 @@ void updateAll() { final int count = mAttachedViews.size(); for (int i = 0; i < count; i++) { DateTimeView view = mAttachedViews.get(i); + if (view.mShowRelativeTime) { + // Every minute, the status bar only needs to execute this function once + // to update the display + view.post(() -> view.updateRelativeTime()); + return; + } + view.post(() -> view.clearFormatAndUpdate()); } } diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java index a328c78f37386..be5cec9beaca6 100644 --- a/core/java/android/widget/ListView.java +++ b/core/java/android/widget/ListView.java @@ -264,8 +264,8 @@ public ListView(Context context, AttributeSet attrs, int defStyleAttr, int defSt } } - mHeaderDividersEnabled = a.getBoolean(R.styleable.ListView_headerDividersEnabled, true); - mFooterDividersEnabled = a.getBoolean(R.styleable.ListView_footerDividersEnabled, true); + mHeaderDividersEnabled = a.getBoolean(R.styleable.ListView_headerDividersEnabled, false); + mFooterDividersEnabled = a.getBoolean(R.styleable.ListView_footerDividersEnabled, false); a.recycle(); } diff --git a/core/java/android/widget/OverScroller.java b/core/java/android/widget/OverScroller.java index df209fb876bab..6afe6248addeb 100644 --- a/core/java/android/widget/OverScroller.java +++ b/core/java/android/widget/OverScroller.java @@ -541,6 +541,9 @@ static class SplineOverScroller { // Current position private int mCurrentPosition; + // last frame position + private int mLastPosition; + // Final position private int mFinal; @@ -912,7 +915,7 @@ boolean update() { final long time = AnimationUtils.currentAnimationTimeMillis(); final long currentTime = time - mStartTime; - if (currentTime == 0) { + if (currentTime <= 0) { // Skip work but report that we're still going if we have a nonzero duration. return mDuration > 0; } @@ -959,7 +962,16 @@ boolean update() { } mCurrentPosition = mStart + (int) Math.round(distance); - + int deltaDistance = mCurrentPosition - mLastPosition; + if (!mFinished && deltaDistance == 0) { + int direction = mFinal > mStart ? 1 : -1; + mCurrentPosition += direction; + if ((direction > 0 && mCurrentPosition > mFinal) || + (direction < 0 && mCurrentPosition < mFinal)) { + mCurrentPosition = mFinal; + } + } + mLastPosition = mCurrentPosition; return true; } } diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java index d54addbbcb8d0..376707deaa721 100644 --- a/core/java/android/widget/PopupWindow.java +++ b/core/java/android/widget/PopupWindow.java @@ -1531,6 +1531,7 @@ private PopupBackgroundView createBackgroundView(View contentView) { final PopupBackgroundView.LayoutParams listParams = new PopupBackgroundView.LayoutParams( MATCH_PARENT, height); backgroundView.addView(contentView, listParams); + backgroundView.setClipToOutline(true); return backgroundView; } diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index e0e9e0b471d5f..35b7f01c6c084 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -869,6 +869,7 @@ private void applyErrorDrawableIfNeeded(int layoutDirection) { // more bold. private int mFontWeightAdjustment; private Typeface mOriginalTypeface; + private String mFontFamily; // True if setKeyListener() has been explicitly called private boolean mListenerChanged = false; @@ -4378,6 +4379,7 @@ private void readTextAppearance(Context context, TypedArray appearance, attributes.mTypefaceIndex = appearance.getInt(attr, attributes.mTypefaceIndex); if (attributes.mTypefaceIndex != -1 && !attributes.mFontFamilyExplicit) { attributes.mFontFamily = null; + mFontFamily = null; } break; case com.android.internal.R.styleable.TextAppearance_fontFamily: @@ -4390,6 +4392,7 @@ private void readTextAppearance(Context context, TypedArray appearance, } if (attributes.mFontTypeface == null) { attributes.mFontFamily = appearance.getString(attr); + mFontFamily = attributes.mFontFamily; } attributes.mFontFamilyExplicit = true; break; @@ -4485,6 +4488,7 @@ private void applyTextAppearance(TextAppearanceAttributes attributes) { if (attributes.mTypefaceIndex != -1 && !attributes.mFontFamilyExplicit) { attributes.mFontFamily = null; + mFontFamily = null; } setTypefaceFromAttrs(attributes.mFontTypeface, attributes.mFontFamily, attributes.mTypefaceIndex, attributes.mTextStyle, attributes.mFontWeight); @@ -4518,6 +4522,10 @@ private void applyTextAppearance(TextAppearanceAttributes attributes) { setFontVariationSettings(attributes.mFontVariationSettings); } + if (Typeface.getFontName().equals("inter")) { + setFontFeatureSettings("'ss01'"); + } + if (attributes.mHasLineBreakStyle || attributes.mHasLineBreakWordStyle) { updateLineBreakConfigFromTextAppearance(attributes.mHasLineBreakStyle, attributes.mHasLineBreakWordStyle, attributes.mLineBreakStyle, @@ -4659,6 +4667,13 @@ protected void onConfigurationChanged(Configuration newConfig) { invalidate(); } } + + if (!TextUtils.equals(mFontFamily, Typeface.getFontName())) { + Typeface tf = Typeface.getOverrideTypeface(mFontFamily); + setTypeface(tf); + mFontFamily = Typeface.getFontName(); + } + if (mFontWeightAdjustment != newConfig.fontWeightAdjustment) { mFontWeightAdjustment = newConfig.fontWeightAdjustment; setTypeface(getTypeface()); diff --git a/core/java/android/window/flags/windowing_frontend.aconfig b/core/java/android/window/flags/windowing_frontend.aconfig index 92dbdc5280e35..26aa27067487f 100644 --- a/core/java/android/window/flags/windowing_frontend.aconfig +++ b/core/java/android/window/flags/windowing_frontend.aconfig @@ -246,17 +246,6 @@ flag { } } -flag { - name: "reduce_unnecessary_measure" - namespace: "windowing_frontend" - description: "Skip measuring view hierarchy if the size is known" - bug: "260382739" - is_fixed_read_only: true - metadata { - purpose: PURPOSE_BUGFIX - } -} - flag { name: "ensure_keyguard_does_transition_starting" namespace: "windowing_frontend" diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index 9bc6671bbc310..29bb65a962b59 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -1440,15 +1440,18 @@ private ViewGroup displayImageContentPreview(Intent targetIntent, LayoutInflater final ViewGroup actionRow = (ViewGroup) contentPreviewLayout.findViewById(R.id.chooser_action_row); + String action = targetIntent.getAction(); + //TODO: addActionButton(actionRow, createCopyButton()); if (shouldNearbyShareBeIncludedAsActionButton()) { addActionButton(actionRow, createNearbyButton(targetIntent)); } - addActionButton(actionRow, createEditButton(targetIntent)); + if (!Intent.ACTION_SEND_MULTIPLE.equals(action)) { + addActionButton(actionRow, createEditButton(targetIntent)); + } mPreviewCoord = new ContentPreviewCoordinator(contentPreviewLayout, false); - String action = targetIntent.getAction(); if (Intent.ACTION_SEND.equals(action)) { Uri uri = targetIntent.getParcelableExtra(Intent.EXTRA_STREAM, android.net.Uri.class); if (!validForContentPreview(uri)) { @@ -2823,7 +2826,7 @@ public Intent getReferrerFillInIntent() { @Override // ChooserListCommunicator public int getMaxRankedTargets() { - return mMaxTargetsPerRow; + return mMaxTargetsPerRow * 2; } @Override // ChooserListCommunicator diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl index f01aa80fab4f7..ab973a3ea9144 100644 --- a/core/java/com/android/internal/app/IAppOpsService.aidl +++ b/core/java/com/android/internal/app/IAppOpsService.aidl @@ -162,6 +162,6 @@ interface IAppOpsService { int attributionFlags, int attributionChainId); void finishOperationForDevice(IBinder clientId, int code, int uid, String packageName, @nullable String attributionTag, int virtualDeviceId); - List getPackagesForOpsForDevice(in int[] ops, String persistentDeviceId); - oneway void noteOperationsInBatch(in Map batchedNoteOps); + ParceledListSlice getPackagesForOpsForDevice(in int[] ops, String persistentDeviceId); + oneway void noteOperationsInBatch(in Map batchedNoteOps); } diff --git a/core/java/com/android/internal/app/LocaleStore.java b/core/java/com/android/internal/app/LocaleStore.java index ceecf787901e4..a76d4ad7cbc31 100644 --- a/core/java/com/android/internal/app/LocaleStore.java +++ b/core/java/com/android/internal/app/LocaleStore.java @@ -31,6 +31,7 @@ import java.io.Serializable; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; @@ -603,7 +604,8 @@ private static Set getTierLocales( boolean hasTargetParent = parent != null; String parentId = hasTargetParent ? parent.getId() : null; HashSet result = new HashSet<>(); - for (LocaleStore.LocaleInfo li : supportedLocaleInfos.values()) { + Collection currentLocaleInfos = new ArrayList<>(supportedLocaleInfos.values()); + for (LocaleStore.LocaleInfo li : currentLocaleInfos) { if (isShallIgnore(ignorables, li, translatedOnly)) { continue; } diff --git a/core/java/com/android/internal/app/MediaRouteDialogPresenter.java b/core/java/com/android/internal/app/MediaRouteDialogPresenter.java index 76b2894160761..d4b12a8d3e275 100644 --- a/core/java/com/android/internal/app/MediaRouteDialogPresenter.java +++ b/core/java/com/android/internal/app/MediaRouteDialogPresenter.java @@ -101,6 +101,7 @@ public static Dialog createDialog(Context context, int routeTypes, public static boolean shouldShowChooserDialog(Context context, int routeTypes) { final MediaRouter router = context.getSystemService(MediaRouter.class); MediaRouter.RouteInfo route = router.getSelectedRoute(); - return route.isDefault() || !route.matchesTypes(routeTypes); + return route.isDefault() || !route.matchesTypes(routeTypes) + || route.getStatusCode() == MediaRouter.RouteInfo.STATUS_NOT_AVAILABLE; } } diff --git a/core/java/com/android/internal/app/SuspendedAppActivity.java b/core/java/com/android/internal/app/SuspendedAppActivity.java index 9d5704141c93a..cf97d9885a2c0 100644 --- a/core/java/com/android/internal/app/SuspendedAppActivity.java +++ b/core/java/com/android/internal/app/SuspendedAppActivity.java @@ -317,7 +317,7 @@ public void onClick(DialogInterface dialog, int which) { try { final String[] errored = ipm.setPackagesSuspendedAsUser( new String[]{mSuspendedPackage}, false, null, null, null, 0, - mSuspendingPackage, mUserId /* suspendingUserId */, + mSuspendingPackage, mSuspendingUserId /* suspendingUserId */, mUserId /* targetUserId */); if (ArrayUtils.contains(errored, mSuspendedPackage)) { Slog.e(TAG, "Could not unsuspend " + mSuspendedPackage); diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java index f06d2004bbbf0..204498d55c4ad 100644 --- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java +++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java @@ -132,6 +132,11 @@ public final class SystemUiDeviceConfigFlags { */ public static final String PROPERTY_MIC_CAMERA_ENABLED = "camera_mic_icons_enabled"; + /** + * Whether to show app ops chip for location. + */ + public static final String PROPERTY_LOCATION_INDICATORS_ENABLED = "location_indicators_enabled"; + /** * Whether to show privacy chip for media projection. */ diff --git a/core/java/com/android/internal/content/F2fsUtils.java b/core/java/com/android/internal/content/F2fsUtils.java index 27f1b308ed9c3..afa8d7773b0b4 100644 --- a/core/java/com/android/internal/content/F2fsUtils.java +++ b/core/java/com/android/internal/content/F2fsUtils.java @@ -266,7 +266,10 @@ private static List getFilesRecursive(@NonNull File path) { final ArrayList files = new ArrayList<>(); for (File f : allFiles) { if (f.isDirectory()) { - files.addAll(getFilesRecursive(f)); + List inner = getFilesRecursive(f); + if (inner != null) { + files.addAll(inner); + } } else if (f.isFile()) { files.add(f); } diff --git a/core/java/com/android/internal/content/om/OverlayConfig.java b/core/java/com/android/internal/content/om/OverlayConfig.java index 38593b4a2a990..537cd0f09ef88 100644 --- a/core/java/com/android/internal/content/om/OverlayConfig.java +++ b/core/java/com/android/internal/content/om/OverlayConfig.java @@ -489,13 +489,14 @@ public String toString() { * precedence. */ @VisibleForTesting - public ArrayList getImmutableFrameworkOverlayIdmapInvocations() { + public ArrayList getImmutableFrameworkOverlayIdmapInvocations( + String packageName) { final ArrayList idmapInvocations = new ArrayList<>(); final ArrayList sortedConfigs = getSortedOverlays(); for (int i = 0, n = sortedConfigs.size(); i < n; i++) { final Configuration overlay = sortedConfigs.get(i); if (overlay.parsedConfig.mutable || !overlay.parsedConfig.enabled - || !"android".equals(overlay.parsedConfig.parsedInfo.targetPackageName)) { + || !packageName.equals(overlay.parsedConfig.parsedInfo.targetPackageName)) { continue; } @@ -534,26 +535,32 @@ public ArrayList getImmutableFrameworkOverlayIdmapInvocations() */ @NonNull public String[] createImmutableFrameworkIdmapsInZygote() { - final String targetPath = AssetManager.FRAMEWORK_APK_PATH; final ArrayList idmapPaths = new ArrayList<>(); - final ArrayList idmapInvocations = - getImmutableFrameworkOverlayIdmapInvocations(); - - for (int i = 0, n = idmapInvocations.size(); i < n; i++) { - final IdmapInvocation invocation = idmapInvocations.get(i); - final String[] idmaps = createIdmap(targetPath, - invocation.overlayPaths.toArray(new String[0]), - new String[]{OverlayConfigParser.OverlayPartition.POLICY_PUBLIC, - invocation.policy}, - invocation.enforceOverlayable); - - if (idmaps == null) { - Log.w(TAG, "'idmap2 create-multiple' failed: no mutable=\"false\" overlays" - + " targeting \"android\" will be loaded"); - return new String[0]; - } - idmapPaths.addAll(Arrays.asList(idmaps)); + for (Map.Entry target : new HashMap() {{ + put(AssetManager.FRAMEWORK_APK_PATH, "android"); + put("/system/framework/org.lineageos.platform-res.apk", "lineageos.platform"); + }}.entrySet()) { + final String targetPath = target.getKey(); + final String targetPackageName = target.getValue(); + final ArrayList idmapInvocations = + getImmutableFrameworkOverlayIdmapInvocations(targetPackageName); + for (int i = 0, n = idmapInvocations.size(); i < n; i++) { + final IdmapInvocation invocation = idmapInvocations.get(i); + final String[] idmaps = createIdmap(targetPath, + invocation.overlayPaths.toArray(new String[0]), + new String[]{OverlayConfigParser.OverlayPartition.POLICY_PUBLIC, + invocation.policy}, + invocation.enforceOverlayable); + + if (idmaps == null) { + Log.w(TAG, "'idmap2 create-multiple' failed: no mutable=\"false\" overlays" + + " targeting \"android\" will be loaded"); + return new String[0]; + } + + idmapPaths.addAll(Arrays.asList(idmaps)); + } } return idmapPaths.toArray(new String[0]); diff --git a/core/java/com/android/internal/graphics/ColorUtils.java b/core/java/com/android/internal/graphics/ColorUtils.java index f210741e070bd..2a38f3098dcc5 100644 --- a/core/java/com/android/internal/graphics/ColorUtils.java +++ b/core/java/com/android/internal/graphics/ColorUtils.java @@ -22,6 +22,7 @@ import android.annotation.NonNull; import android.graphics.Color; import android.ravenwood.annotation.RavenwoodKeepWholeClass; +import android.util.Log; import com.android.internal.graphics.cam.Cam; /** @@ -32,6 +33,8 @@ @RavenwoodKeepWholeClass public final class ColorUtils { + private static final String TAG = "ColorUtils"; + private static final double XYZ_WHITE_REFERENCE_X = 95.047; private static final double XYZ_WHITE_REFERENCE_Y = 100; private static final double XYZ_WHITE_REFERENCE_Z = 108.883; @@ -96,8 +99,10 @@ public static double calculateLuminance(@ColorInt int color) { */ public static double calculateContrast(@ColorInt int foreground, @ColorInt int background) { if (Color.alpha(background) != 255) { - throw new IllegalArgumentException("background can not be translucent: #" - + Integer.toHexString(background)); + Log.w(TAG, String.format( + "Background should not be translucent: #%s", + Integer.toHexString(background))); + background = setAlphaComponent(background, 255); } if (Color.alpha(foreground) < 255) { // If the foreground is translucent, composite the foreground over the background @@ -149,8 +154,10 @@ public static int calculateMinimumBackgroundAlpha(@ColorInt int foreground, public static int calculateMinimumAlpha(@ColorInt int foreground, @ColorInt int background, float minContrastRatio) { if (Color.alpha(background) != 255) { - throw new IllegalArgumentException("background can not be translucent: #" - + Integer.toHexString(background)); + Log.w(TAG, String.format( + "Background should not be translucent: #%s", + Integer.toHexString(background))); + background = setAlphaComponent(background, 255); } ContrastCalculator contrastCalculator = (fg, bg, alpha) -> { diff --git a/core/java/com/android/internal/net/VpnProfile.java b/core/java/com/android/internal/net/VpnProfile.java index f62094d231efd..3bd8df7b3a630 100644 --- a/core/java/com/android/internal/net/VpnProfile.java +++ b/core/java/com/android/internal/net/VpnProfile.java @@ -496,6 +496,21 @@ public static boolean isLegacyType(int type) { } } + /** + * Returns {@code true} if the VPN type requires username and password. + * + *

In sync with Settings's ConfigDialog. + */ + public boolean isUsernamePasswordRequiredVpnProfile() { + switch (type) { + case VpnProfile.TYPE_IKEV2_IPSEC_RSA: // fall through + case VpnProfile.TYPE_IKEV2_IPSEC_PSK: + return false; + default: + return true; + } + } + private boolean isValidLockdownLegacyVpnProfile() { return isLegacyType(type) && isServerAddressNumeric() && hasDns() && areDnsAddressesNumeric(); diff --git a/core/java/com/android/internal/os/DeviceKeyHandler.java b/core/java/com/android/internal/os/DeviceKeyHandler.java new file mode 100644 index 0000000000000..8902337f3ebb3 --- /dev/null +++ b/core/java/com/android/internal/os/DeviceKeyHandler.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2012 The CyanogenMod Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.os; + +import android.view.KeyEvent; + +public interface DeviceKeyHandler { + + /** + * Invoked when an unknown key was detected by the system, letting the device handle + * this special keys prior to pass the key to the active app. + * + * @param event The key event to be handled + * @return null if event is consumed, KeyEvent to be handled otherwise + */ + public KeyEvent handleKeyEvent(KeyEvent event); +} diff --git a/core/java/com/android/internal/os/KernelCpuUidTimeReader.java b/core/java/com/android/internal/os/KernelCpuUidTimeReader.java index 21e0dc59db01d..b0b9c2cfddb4a 100644 --- a/core/java/com/android/internal/os/KernelCpuUidTimeReader.java +++ b/core/java/com/android/internal/os/KernelCpuUidTimeReader.java @@ -452,7 +452,7 @@ private void processUidDelta(@Nullable Callback cb) { // Unit is 10ms. mDeltaTimes[i] = mCurTimes[i] - lastTimes[i]; if (mDeltaTimes[i] < 0) { - Slog.e(mTag, "Negative delta from freq time for uid: " + uid + if (DEBUG) Slog.e(mTag, "Negative delta from freq time for uid: " + uid + ", delta: " + mDeltaTimes[i]); return; } @@ -628,7 +628,7 @@ private void processUidDelta(@Nullable Callback cb) { cb.onUidCpuTime(uid, delta); } } else if (delta < 0) { - Slog.e(mTag, "Negative delta from active time for uid: " + uid + if (DEBUG) Slog.e(mTag, "Negative delta from active time for uid: " + uid + ", delta: " + delta); } } diff --git a/core/java/com/android/internal/os/PowerStats.java b/core/java/com/android/internal/os/PowerStats.java index 6c69e2c623ad4..01e3cbded9d0d 100644 --- a/core/java/com/android/internal/os/PowerStats.java +++ b/core/java/com/android/internal/os/PowerStats.java @@ -52,12 +52,12 @@ public final class PowerStats { private static final BatteryStatsHistory.VarintParceler VARINT_PARCELER = new BatteryStatsHistory.VarintParceler(); - private static final byte PARCEL_FORMAT_VERSION = 2; + private static final byte PARCEL_FORMAT_VERSION = 3; - private static final int PARCEL_FORMAT_VERSION_MASK = 0x000000FF; + private static final int PARCEL_FORMAT_VERSION_MASK = 0x0000003F; private static final int PARCEL_FORMAT_VERSION_SHIFT = Integer.numberOfTrailingZeros(PARCEL_FORMAT_VERSION_MASK); - private static final int STATS_ARRAY_LENGTH_MASK = 0x0000FF00; + private static final int STATS_ARRAY_LENGTH_MASK = 0x0000FFC0; private static final int STATS_ARRAY_LENGTH_SHIFT = Integer.numberOfTrailingZeros(STATS_ARRAY_LENGTH_MASK); public static final int MAX_STATS_ARRAY_LENGTH = diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java index f6de3459a1f58..34b12b0344e85 100644 --- a/core/java/com/android/internal/os/RuntimeInit.java +++ b/core/java/com/android/internal/os/RuntimeInit.java @@ -167,8 +167,11 @@ public void uncaughtException(Thread t, Throwable e) { } // Bring up crash dialog, wait for it to be dismissed - ActivityManager.getService().handleApplicationCrash( - mApplicationObject, new ApplicationErrorReport.ParcelableCrashInfo(e)); + final IActivityManager am = ActivityManager.getService(); + if (am != null) { + am.handleApplicationCrash( + mApplicationObject, new ApplicationErrorReport.ParcelableCrashInfo(e)); + } } catch (Throwable t2) { if (t2 instanceof DeadObjectException) { // System process is dead; ignore diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java index f385931adbd25..edba6f38fdbf3 100644 --- a/core/java/com/android/internal/os/ZygoteInit.java +++ b/core/java/com/android/internal/os/ZygoteInit.java @@ -92,7 +92,7 @@ public class ZygoteInit { private static final String TAG = "Zygote"; - private static final boolean LOGGING_DEBUG = Log.isLoggable(TAG, Log.DEBUG); + private static final boolean LOGGING_DEBUG = false; private static final String PROPERTY_DISABLE_GRAPHICS_DRIVER_PRELOADING = "ro.zygote.disable_gl_preload"; diff --git a/core/java/com/android/internal/pm/pkg/component/AconfigFlags.java b/core/java/com/android/internal/pm/pkg/component/AconfigFlags.java index 13b71e71bab4e..dc58171f59f5f 100644 --- a/core/java/com/android/internal/pm/pkg/component/AconfigFlags.java +++ b/core/java/com/android/internal/pm/pkg/component/AconfigFlags.java @@ -101,7 +101,8 @@ public AconfigFlags() { } private static boolean useNewStorage() { - return newStoragePublicApi() && Flags.useNewAconfigStorage(); + return newStoragePublicApi() && Flags.useNewAconfigStorage() && + Environment.getMetadataDirectory().exists(); } private void loadServerOverrides() { diff --git a/core/java/com/android/internal/policy/GestureNavigationSettingsObserver.java b/core/java/com/android/internal/policy/GestureNavigationSettingsObserver.java index 260619ec0b235..b0353ee85cea8 100644 --- a/core/java/com/android/internal/policy/GestureNavigationSettingsObserver.java +++ b/core/java/com/android/internal/policy/GestureNavigationSettingsObserver.java @@ -74,11 +74,22 @@ public void register() { r.registerContentObserver( Settings.Secure.getUriFor(Settings.Secure.USER_SETUP_COMPLETE), false, this, UserHandle.USER_ALL); + r.registerContentObserver( + Settings.Secure.getUriFor(Settings.Secure.BACK_GESTURE_ARROW), + false, this, UserHandle.USER_ALL); + r.registerContentObserver( + Settings.Secure.getUriFor(Settings.Secure.BACK_GESTURE_HAPTIC), + false, this, UserHandle.USER_ALL); DeviceConfig.addOnPropertiesChangedListener( DeviceConfig.NAMESPACE_SYSTEMUI, runnable -> mMainHandler.post(runnable), mOnPropertiesChangedListener); + + r.registerContentObserver( + Settings.System.getUriFor(Settings.System.LOCK_GESTURE_STATUS), + false, this, UserHandle.USER_ALL); }); + } /** @@ -160,6 +171,16 @@ public int getRightSensitivityForCallingUser(Resources userRes) { return (int) (getUnscaledInset(userRes) * scale); } + public boolean getBackArrowGesture() { + return Settings.Secure.getIntForUser(mContext.getContentResolver(), + Settings.Secure.BACK_GESTURE_ARROW, 1, UserHandle.USER_CURRENT) != 0; + } + + public boolean getEdgeHapticEnabled() { + return Settings.Secure.getIntForUser(mContext.getContentResolver(), + Settings.Secure.BACK_GESTURE_HAPTIC, 1, UserHandle.USER_CURRENT) != 0; + } + public boolean areNavigationButtonForcedVisible() { String SUWTheme = SystemProperties.get("setupwizard.theme", ""); boolean isExpressiveThemeEnabled = SUWTheme.equals("glif_expressive") diff --git a/core/java/com/android/internal/policy/PhoneFallbackEventHandler.java b/core/java/com/android/internal/policy/PhoneFallbackEventHandler.java index 04dd2d72729d2..8ce39d703eff1 100644 --- a/core/java/com/android/internal/policy/PhoneFallbackEventHandler.java +++ b/core/java/com/android/internal/policy/PhoneFallbackEventHandler.java @@ -143,18 +143,6 @@ boolean onKeyDown(int keyCode, KeyEvent event) { dispatcher.startTracking(event, this); } else if (event.isLongPress() && dispatcher.isTracking(event)) { dispatcher.performedLongPress(event); - if (isUserSetupComplete()) { - mView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); - // Broadcast an intent that the Camera button was longpressed - Intent intent = new Intent(Intent.ACTION_CAMERA_BUTTON, null); - intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); - intent.putExtra(Intent.EXTRA_KEY_EVENT, event); - mContext.sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT_OR_SELF, - null, null, null, 0, null, null); - } else { - Log.i(TAG, "Not dispatching CAMERA long press because user " - + "setup is in progress."); - } } return true; } diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java index 72217b0ed2637..40578bf1db781 100644 --- a/core/java/com/android/internal/policy/PhoneWindow.java +++ b/core/java/com/android/internal/policy/PhoneWindow.java @@ -34,6 +34,7 @@ import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS; import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT; +import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_EDGE_TO_EDGE_ENFORCED; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION; @@ -2733,6 +2734,16 @@ protected ViewGroup generateLayout(DecorView decor) { params.layoutInDisplayCutoutMode = mode; } + if (ActivityManager.isSystemReady()) { + try { + String packageName = context.getBasePackageName(); + if (ActivityManager.getService().shouldForceCutoutFullscreen(packageName)){ + params.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; + } + } catch (RemoteException e) { + } + } + if (mAlwaysReadCloseOnTouchAttr || getContext().getApplicationInfo().targetSdkVersion >= android.os.Build.VERSION_CODES.HONEYCOMB) { if (a.getBoolean( diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl index 08626c8773fc8..c0713dd1d16b8 100644 --- a/core/java/com/android/internal/statusbar/IStatusBar.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl @@ -190,7 +190,7 @@ oneway interface IStatusBar void showPinningEnterExitToast(boolean entering); void showPinningEscapeToast(); - void showShutdownUi(boolean isReboot, String reason); + void showShutdownUi(boolean isReboot, String reason, boolean rebootCustom); /** * Used to show the authentication dialog (Biometrics, Device Credential). @@ -413,4 +413,6 @@ oneway interface IStatusBar * @param displayId the id of the current display. */ void moveFocusedTaskToDesktop(int displayId); + + void restartSystemUI(); } diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl index d96f1f8e7e5e0..aa40e92d74b72 100644 --- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl @@ -116,7 +116,7 @@ interface IStatusBarService * These methods are needed for global actions control which the UI is shown in sysui. */ void shutdown(); - void reboot(boolean safeMode); + void reboot(boolean safeMode, String reason); /** just restarts android without rebooting device. Used for some feature flags. */ void restart(); @@ -244,4 +244,16 @@ interface IStatusBarService /** Shows rear display educational dialog */ void showRearDisplayDialog(int currentBaseState); + + /** + * Starts the default assistant app. + */ + void startAssist(in Bundle args); + + /** + * Toggle recent apps. + */ + void toggleRecentApps(); + + void restartSystemUI(); } diff --git a/core/java/com/android/internal/util/ArtFastDataOutput.java b/core/java/com/android/internal/util/ArtFastDataOutput.java index 360ddb814aa26..672bdbbcbfcd1 100644 --- a/core/java/com/android/internal/util/ArtFastDataOutput.java +++ b/core/java/com/android/internal/util/ArtFastDataOutput.java @@ -84,6 +84,10 @@ public byte[] newByteArray(int bufferSize) { public void writeUTF(String s) throws IOException { // Attempt to write directly to buffer space if there's enough room, // otherwise fall back to chunking into place + if (s == null) { + return; + } + if (mBufferCap - mBufferPos < 2 + s.length()) drain(); // Magnitude of this returned value indicates the number of bytes diff --git a/core/java/com/android/internal/util/ContrastColorUtil.java b/core/java/com/android/internal/util/ContrastColorUtil.java index c68f107951ac7..6864f78148f1c 100644 --- a/core/java/com/android/internal/util/ContrastColorUtil.java +++ b/core/java/com/android/internal/util/ContrastColorUtil.java @@ -809,8 +809,10 @@ public static double calculateLuminance(@ColorInt int color) { */ public static double calculateContrast(@ColorInt int foreground, @ColorInt int background) { if (Color.alpha(background) != 255) { - Log.wtf(TAG, "background can not be translucent: #" - + Integer.toHexString(background)); + Log.w(TAG, String.format( + "Background should not be translucent: #%s", + Integer.toHexString(background))); + background = setAlphaComponent(background, 255); } if (Color.alpha(foreground) < 255) { // If the foreground is translucent, composite the foreground over the background diff --git a/core/java/com/android/internal/util/FileRotator.java b/core/java/com/android/internal/util/FileRotator.java index bcad6fc85ca92..22014a411e49c 100644 --- a/core/java/com/android/internal/util/FileRotator.java +++ b/core/java/com/android/internal/util/FileRotator.java @@ -334,7 +334,15 @@ private String getActiveName(long currentTimeMillis) { long oldestActiveStart = Long.MAX_VALUE; final FileInfo info = new FileInfo(mPrefix); - for (String name : mBasePath.list()) { + String[] baseFiles = mBasePath.list(); + if (baseFiles == null) { + // no file in the path and create one starting now + info.startMillis = currentTimeMillis; + info.endMillis = Long.MAX_VALUE; + return info.build(); + } + + for (String name : baseFiles) { if (!info.parse(name)) continue; // pick the oldest active file which covers current time diff --git a/core/java/com/android/internal/util/PastyException.java b/core/java/com/android/internal/util/PastyException.java new file mode 100644 index 0000000000000..e956bb20dfc08 --- /dev/null +++ b/core/java/com/android/internal/util/PastyException.java @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2018-2025 crDroid Android Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.util; + +public class PastyException extends Exception { + + private static final long serialVersionUID = 666L; + + public PastyException(String message) { + super(message); + } +} diff --git a/core/java/com/android/internal/util/PastyUtils.java b/core/java/com/android/internal/util/PastyUtils.java new file mode 100644 index 0000000000000..617b682cc802e --- /dev/null +++ b/core/java/com/android/internal/util/PastyUtils.java @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2018-2025 crDroid Android Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.util; + +import android.os.Handler; +import android.os.HandlerThread; +import android.util.JsonReader; + +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.net.URL; + +import javax.net.ssl.HttpsURLConnection; + +/** + * Helper functions for uploading to Pasty + */ +public final class PastyUtils { + private static final String TAG = "PastyUtils"; + private static final String BASE_URL = "https://paste.crdroid.net"; + private static final String API_URL = String.format("%s/documents", BASE_URL); + private static Handler handler; + + private PastyUtils() { + } + + /** + * Uploads {@code content} to Pasty + * + * @param content the content to upload to Pasty + * @param callback the callback to call on success / failure + */ + public static void upload(String content, UploadResultCallback callback) { + getHandler().post(new Runnable() { + @Override + public void run() { + try { + HttpsURLConnection urlConnection = (HttpsURLConnection) new URL(API_URL).openConnection(); + try { + urlConnection.setRequestProperty("Content-Type", "text/plain"); + urlConnection.setRequestProperty("Accept-Charset", "UTF-8"); + urlConnection.setDoOutput(true); + + try (OutputStream output = urlConnection.getOutputStream()) { + output.write(content.getBytes("UTF-8")); + } + String key = ""; + try (JsonReader reader = new JsonReader( + new InputStreamReader(urlConnection.getInputStream(), "UTF-8"))) { + reader.beginObject(); + while (reader.hasNext()) { + String name = reader.nextName(); + if (name.equals("key")) { + key = reader.nextString(); + break; + } else { + reader.skipValue(); + } + } + reader.endObject(); + } + if (!key.isEmpty()) { + callback.onSuccess(getUrl(key)); + } else { + String msg = "Failed to upload to Pasty: No key retrieved"; + callback.onFail(msg, new PastyException(msg)); + } + } finally { + urlConnection.disconnect(); + } + } catch (Exception e) { + callback.onFail("Failed to upload to Pasty", e); + } + } + }); + } + + /** + * Get the view URL from a key + */ + private static String getUrl(String key) { + return String.format("%s/%s", BASE_URL, key); + } + + private static Handler getHandler() { + if (handler == null) { + HandlerThread handlerThread = new HandlerThread("PastyThread"); + if (!handlerThread.isAlive()) + handlerThread.start(); + handler = new Handler(handlerThread.getLooper()); + } + return handler; + } + + public interface UploadResultCallback { + void onSuccess(String url); + + void onFail(String message, Exception e); + } +} diff --git a/core/java/com/android/internal/util/ScreenshotHelper.java b/core/java/com/android/internal/util/ScreenshotHelper.java index c21a43e807a9d..0c3ac405c2098 100644 --- a/core/java/com/android/internal/util/ScreenshotHelper.java +++ b/core/java/com/android/internal/util/ScreenshotHelper.java @@ -20,6 +20,7 @@ import android.os.UserHandle; import android.util.Log; import android.view.WindowManager.ScreenshotSource; +import android.view.WindowManager.ScreenshotType; import com.android.internal.annotations.VisibleForTesting; @@ -68,8 +69,25 @@ public ScreenshotHelper(Context context) { */ public void takeScreenshot(@ScreenshotSource int source, @NonNull Handler handler, @Nullable Consumer completionConsumer) { + takeScreenshot(TAKE_SCREENSHOT_FULLSCREEN, source, handler, completionConsumer); + } + + /** + * Request a screenshot be taken. + *

+ * Convenience method for taking a full screenshot with provided source. + * + * @param type type of screenshot, defined by {@link ScreenshotType} + * @param source source of the screenshot request, defined by {@link + * ScreenshotSource} + * @param handler used to process messages received from the screenshot service + * @param completionConsumer receives the URI of the captured screenshot, once saved or + * null if no screenshot was saved + */ + public void takeScreenshot(@ScreenshotType int type, @ScreenshotSource int source, + @NonNull Handler handler, @Nullable Consumer completionConsumer) { ScreenshotRequest request = - new ScreenshotRequest.Builder(TAKE_SCREENSHOT_FULLSCREEN, source).build(); + new ScreenshotRequest.Builder(type, source).build(); takeScreenshot(request, handler, completionConsumer); } diff --git a/core/java/com/android/internal/util/ScreenshotRequest.java b/core/java/com/android/internal/util/ScreenshotRequest.java index 702e5e26ffc1c..a00286c128f2a 100644 --- a/core/java/com/android/internal/util/ScreenshotRequest.java +++ b/core/java/com/android/internal/util/ScreenshotRequest.java @@ -20,6 +20,7 @@ import static android.os.UserHandle.USER_NULL; import static android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN; import static android.view.WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE; +import static android.view.WindowManager.TAKE_SCREENSHOT_SELECTED_REGION; import android.annotation.NonNull; import android.content.ComponentName; @@ -183,7 +184,8 @@ public static class Builder { public Builder( @WindowManager.ScreenshotType int type, @WindowManager.ScreenshotSource int source) { - if (type != TAKE_SCREENSHOT_FULLSCREEN && type != TAKE_SCREENSHOT_PROVIDED_IMAGE) { + if (type != TAKE_SCREENSHOT_FULLSCREEN && type != TAKE_SCREENSHOT_PROVIDED_IMAGE + && type != TAKE_SCREENSHOT_SELECTED_REGION) { throw new IllegalArgumentException("Invalid screenshot type requested!"); } mType = type; @@ -197,6 +199,9 @@ public ScreenshotRequest build() { if (mType == TAKE_SCREENSHOT_FULLSCREEN && mBitmap != null) { Log.w(TAG, "Bitmap provided, but request is fullscreen. Bitmap will be ignored."); } + if (mType == TAKE_SCREENSHOT_SELECTED_REGION && mBitmap != null) { + Log.w(TAG, "Bitmap provided, but request is partial. Bitmap will be ignored."); + } if (mType == TAKE_SCREENSHOT_PROVIDED_IMAGE && mBitmap == null) { throw new IllegalStateException( "Request is PROVIDED_IMAGE, but no bitmap is provided!"); diff --git a/core/java/com/android/internal/util/android/FontController.java b/core/java/com/android/internal/util/android/FontController.java new file mode 100644 index 0000000000000..a684b6daeb12e --- /dev/null +++ b/core/java/com/android/internal/util/android/FontController.java @@ -0,0 +1,259 @@ +/* + * Copyright (C) 2025 AxionOS + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.util.android; + +import android.app.ActivityThread; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.graphics.Typeface; +import android.os.SystemProperties; +import android.util.ArrayMap; +import android.util.Log; +import android.text.TextUtils; +import com.android.internal.R; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Map; +import java.util.regex.Pattern; +import java.util.Set; + +public class FontController { + + private static final String TAG = "FontController"; + + private static FontController sInstance = null; + + private volatile Resources sResources = null; + + private volatile String sFontFamily = "sans-serif"; + + private static final Set OVERRIDE_FONTS = new HashSet<>(Arrays.asList( + "google", "sans-serif", "gsf-" + )); + + private static final Set SYS_OVERRIDE_FONTS = new HashSet<>(Arrays.asList( + "serif", "monospace", "variable" + )); + + private static final Set EXCLUDED_APPS = new HashSet<>(Arrays.asList( + "it.subito", + "tv.arte.plus7", + "com.google.android.gm" + )); + + private static final Set SYS_OVERRIDE_APPS = new HashSet<>(Arrays.asList( + "com.android.settings", + "com.android.systemui", + "com.android.launcher3", + "android" + )); + + private static final Map WEIGHT_MAP = new ArrayMap<>(); + static { + WEIGHT_MAP.put("thin", 100); + WEIGHT_MAP.put("extralight", 200); + WEIGHT_MAP.put("light", 300); + WEIGHT_MAP.put("normal", 400); + WEIGHT_MAP.put("regular", 400); + WEIGHT_MAP.put("medium", 500); + WEIGHT_MAP.put("semibold", 600); + WEIGHT_MAP.put("bold", 700); + WEIGHT_MAP.put("extrabold", 800); + WEIGHT_MAP.put("black", 900); + + WEIGHT_MAP.put("variable-display-large-emphasized", 500); + WEIGHT_MAP.put("variable-display-medium-emphasized", 500); + WEIGHT_MAP.put("variable-display-small-emphasized", 500); + WEIGHT_MAP.put("variable-headline-large-emphasized", 500); + WEIGHT_MAP.put("variable-headline-medium-emphasized", 500); + WEIGHT_MAP.put("variable-headline-small-emphasized", 500); + WEIGHT_MAP.put("variable-title-large-emphasized", 500); + WEIGHT_MAP.put("variable-title-medium-emphasized", 600); + WEIGHT_MAP.put("variable-title-small-emphasized", 600); + WEIGHT_MAP.put("variable-label-large-emphasized", 600); + WEIGHT_MAP.put("variable-label-medium-emphasized", 600); + WEIGHT_MAP.put("variable-label-small-emphasized", 600); + WEIGHT_MAP.put("variable-body-large-emphasized", 500); + WEIGHT_MAP.put("variable-body-medium-emphasized", 500); + WEIGHT_MAP.put("variable-body-small-emphasized", 500); + + WEIGHT_MAP.put("variable-display-large", 400); + WEIGHT_MAP.put("variable-display-medium", 400); + WEIGHT_MAP.put("variable-display-small", 400); + WEIGHT_MAP.put("variable-headline-large", 400); + WEIGHT_MAP.put("variable-headline-medium", 400); + WEIGHT_MAP.put("variable-headline-small", 400); + WEIGHT_MAP.put("variable-title-large", 400); + WEIGHT_MAP.put("variable-title-medium", 500); + WEIGHT_MAP.put("variable-title-small", 500); + WEIGHT_MAP.put("variable-label-large", 500); + WEIGHT_MAP.put("variable-label-medium", 500); + WEIGHT_MAP.put("variable-label-small", 500); + WEIGHT_MAP.put("variable-body-large", 400); + WEIGHT_MAP.put("variable-body-medium", 400); + WEIGHT_MAP.put("variable-body-small", 400); + } + + public static FontController get() { + if (sInstance == null) { + sInstance = new FontController(); + } + return sInstance; + } + + private FontController() {} + + public static void OnConfigurationChanged(Resources res) { + get().handleOnConfiguration(res); + } + + public static Typeface getOverrideTypeface(String fontToOverride) { + return get().getOverrideTypefaceInternal(fontToOverride); + } + + public static String getCurrentFontFamily() { + return get().getCurrentFont(); + } + + private String getCurrentFont() { + if (sResources == null) return sFontFamily; + try { + int configId = sResources.getIdentifier("config_bodyFontFamily", "string", "android"); + if (configId != 0) { + String currFont = sResources.getString(configId); + if (!TextUtils.equals(sFontFamily, currFont)) { + sFontFamily = currFont; + logger("Font changed to: " + sFontFamily); + } + } + } catch (Exception e) { + logger("getCurrentFont failed: " + e.getMessage()); + } + return sFontFamily; + } + + private Typeface getOverrideTypefaceInternal(String fontToOverride) { + if (fontToOverride == null) return null; + + String pkgName = getCurrentPackageName(); + + if (pkgName == null) return null; + + final boolean isSysPkg = SYS_OVERRIDE_APPS.contains(pkgName); + + if (pkgName != null && EXCLUDED_APPS.contains(pkgName) && !isSysPkg) { + logger("Excluded app, skipping override: " + pkgName); + return null; + } + + String currentFont = getCurrentFont(); + + if (fontToOverride.matches("^" + Pattern.quote(currentFont) + "(-.*)?$")) { + logger(fontToOverride + " matches current font root '" + currentFont + "', skipping override!"); + return null; + } + + boolean override = OVERRIDE_FONTS.stream().anyMatch(fontToOverride::contains) + || (isSysPkg && SYS_OVERRIDE_FONTS.stream().anyMatch(fontToOverride::contains)); + if (!override) { + logger("Not on override list, skipping override: " + fontToOverride); + return null; + } + + int adjustment = getFontWeightAdjustment(); + return TypefaceFactory.create(fontToOverride, currentFont, adjustment); + } + + private void handleOnConfiguration(Resources res) { + sResources = res; + String pkgName = getCurrentPackageName(); + if (pkgName == null || EXCLUDED_APPS.contains(pkgName)) return; + logger("handleOnConfiguration: Changing default font to: " + Typeface.getFontName()); + Typeface.changeFont(); + } + + private String getCurrentPackageName() { + try { + return ActivityThread.currentPackageName(); + } catch (Exception e) { + logger("getCurrentPackageName failed: " + e.getMessage()); + return null; + } + } + + private int getFontWeightAdjustment() { + try { + Resources res = sResources; + if (res == null) return 0; + Configuration cfg = res.getConfiguration(); + return cfg != null ? cfg.fontWeightAdjustment : 0; + } catch (Exception e) { + logger("getFontWeightAdjustment failed: " + e.getMessage()); + return 0; + } + } + + private static void logger(String msg) { + if (SystemProperties.getBoolean("persist.sys.ax_font_debug", false)) { + Log.d(TAG, msg); + } + } + + private static class TypefaceFactory { + + public static Typeface create(String fontToOverride, String currentFont, int fontWeightAdjustment) { + int weight = resolveWeightByName(fontToOverride); + + if (fontWeightAdjustment != 0) { + weight = Math.min(1000, Math.max(100, weight + fontWeightAdjustment)); + } + + boolean isBold = weight >= 700; + boolean isItalic = fontToOverride.contains("italic"); + + int style = Typeface.NORMAL; + if (isBold && isItalic) style = Typeface.BOLD_ITALIC; + else if (isBold) style = Typeface.BOLD; + else if (isItalic) style = Typeface.ITALIC; + + Typeface base = Typeface.getSystemDefaultTypeface(currentFont); + Typeface result = Typeface.create(base, style); + result = Typeface.create(result, weight, isItalic); + + logger("TypefaceFactory.create: fontToOverride=" + fontToOverride + + ", style=" + style + + ", weight=" + weight + + ", adj=" + fontWeightAdjustment + + ", isItalic=" + isItalic + + ", success=" + (result != null)); + + return result; + } + + private static int resolveWeightByName(String familyName) { + Integer exactMatch = WEIGHT_MAP.get(familyName); + if (exactMatch != null) { + return exactMatch; + } + for (Map.Entry entry : WEIGHT_MAP.entrySet()) { + if (familyName.contains(entry.getKey())) { + return entry.getValue(); + } + } + return 400; + } + } +} diff --git a/core/java/com/android/internal/util/crdroid/DeviceConfigUtils.java b/core/java/com/android/internal/util/crdroid/DeviceConfigUtils.java new file mode 100644 index 0000000000000..115c5199def89 --- /dev/null +++ b/core/java/com/android/internal/util/crdroid/DeviceConfigUtils.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2023 The Pixel Experience Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.util.crdroid; + +import android.content.res.Resources; +import android.provider.Settings; +import android.util.Log; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import com.android.internal.util.ArrayUtils; + +public class DeviceConfigUtils { + + private static final String TAG = DeviceConfigUtils.class.getSimpleName(); + + private static final boolean DEBUG = false; + + private static String[] getDeviceConfigsOverride() { + String[] globalDeviceConfigs = + Resources.getSystem().getStringArray(com.android.internal.R.array.global_device_configs_override); + String[] deviceConfigs = + Resources.getSystem().getStringArray(com.android.internal.R.array.device_configs_override); + String[] allDeviceConfigs = Arrays.copyOf(globalDeviceConfigs, globalDeviceConfigs.length + deviceConfigs.length); + System.arraycopy(deviceConfigs, 0, allDeviceConfigs, globalDeviceConfigs.length, deviceConfigs.length); + return allDeviceConfigs; + } + + public static boolean shouldDenyDeviceConfigControl(String namespace, String property) { + if (DEBUG) Log.d(TAG, "shouldAllowDeviceConfigControl, namespace=" + namespace + ", property=" + property); + for (String p : getDeviceConfigsOverride()) { + String[] kv = p.split("="); + String fullKey = kv[0]; + String[] nsKey = fullKey.split("/"); + if (nsKey[0] == namespace && nsKey[1] == property){ + if (DEBUG) Log.d(TAG, "shouldAllowDeviceConfigControl, deny, namespace=" + namespace + ", property=" + property); + return true; + } + } + if (DEBUG) Log.d(TAG, "shouldAllowDeviceConfigControl, allow, namespace=" + namespace + ", property=" + property); + return false; + } + + public static void setDefaultProperties(String filterNamespace, String filterProperty) { + if (DEBUG) Log.d(TAG, "setDefaultProperties"); + for (String p : getDeviceConfigsOverride()) { + String[] kv = p.split("="); + String fullKey = kv[0]; + String[] nsKey = fullKey.split("/"); + + String namespace = nsKey[0]; + String key = nsKey[1]; + + if (filterNamespace != null && filterNamespace == namespace){ + continue; + } + + if (filterProperty != null && filterProperty == key){ + continue; + } + + String value = ""; + if (kv.length > 1) { + value = kv[1]; + } + Settings.Config.putString(namespace, key, value, false); + } + } +} diff --git a/core/java/com/android/internal/util/crdroid/IKeyboxProvider.java b/core/java/com/android/internal/util/crdroid/IKeyboxProvider.java new file mode 100644 index 0000000000000..d0f7c2793fd76 --- /dev/null +++ b/core/java/com/android/internal/util/crdroid/IKeyboxProvider.java @@ -0,0 +1,56 @@ +/* + * SPDX-FileCopyrightText: 2024 Paranoid Android + * SPDX-License-Identifier: Apache-2.0 + */ +package com.android.internal.util.crdroid; + +/** + * Interface for keybox providers. + * + * This interface defines the methods that a keybox provider must implement + * to provide access to EC and RSA keys and certificate chains. + * + * @hide + */ +public interface IKeyboxProvider { + + /** + * Checks if a valid keybox is available. + * + * @return true if a valid keybox is available, false otherwise + * @hide + */ + boolean hasKeybox(); + + /** + * Retrieves the EC private key. + * + * @return the EC private key as a String + * @hide + */ + String getEcPrivateKey(); + + /** + * Retrieves the RSA private key. + * + * @return the RSA private key as a String + * @hide + */ + String getRsaPrivateKey(); + + /** + * Retrieves the EC certificate chain. + * + * @return an array of Strings representing the EC certificate chain + * @hide + */ + String[] getEcCertificateChain(); + + /** + * Retrieves the RSA certificate chain. + * + * @return an array of Strings representing the RSA certificate chain + * @hide + */ + String[] getRsaCertificateChain(); +} diff --git a/core/java/com/android/internal/util/crdroid/ImageHelper.java b/core/java/com/android/internal/util/crdroid/ImageHelper.java new file mode 100644 index 0000000000000..23ab5215d5e6f --- /dev/null +++ b/core/java/com/android/internal/util/crdroid/ImageHelper.java @@ -0,0 +1,487 @@ +/* +* Copyright (C) 2013 SlimRoms Project +* Copyright (C) 2015 TeamEos Project +* Copyright (C) 2015-2016 The DirtyUnicorns Project +* Copyright (C) 2019-2025 crDroid Android Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +package com.android.internal.util.crdroid; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileDescriptor; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import android.content.Context; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.Bitmap.Config; +import android.graphics.BitmapFactory; +import android.graphics.BitmapShader; +import android.graphics.Canvas; +import android.graphics.ColorMatrix; +import android.graphics.ColorMatrixColorFilter; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; +import android.graphics.PorterDuffXfermode; +import android.graphics.PorterDuff.Mode; +import android.graphics.Rect; +import android.graphics.RectF; +import android.graphics.Shader.TileMode; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.VectorDrawable; +import android.media.ExifInterface; +import android.net.Uri; +import android.os.ParcelFileDescriptor; +import android.renderscript.Element; +import android.renderscript.Allocation; +import android.renderscript.ScriptIntrinsicBlur; +import android.renderscript.RenderScript; +import android.util.AttributeSet; +import android.util.Log; +import android.util.TypedValue; +import android.util.Xml; + +public class ImageHelper { + private static final int VECTOR_WIDTH = 512; + private static final int VECTOR_HEIGHT = 512; + + public static Drawable getColoredDrawable(Drawable d, int color) { + if (d == null) { + return null; + } + if (d instanceof VectorDrawable) { + d.setTint(color); + return d; + } + Bitmap colorBitmap = ((BitmapDrawable) d).getBitmap(); + Bitmap grayscaleBitmap = toGrayscale(colorBitmap); + Paint pp = new Paint(); + pp.setAntiAlias(true); + PorterDuffColorFilter frontFilter = + new PorterDuffColorFilter(color, Mode.MULTIPLY); + pp.setColorFilter(frontFilter); + Canvas cc = new Canvas(grayscaleBitmap); + final Rect rect = new Rect(0, 0, grayscaleBitmap.getWidth(), grayscaleBitmap.getHeight()); + cc.drawBitmap(grayscaleBitmap, rect, rect, pp); + return new BitmapDrawable(grayscaleBitmap); + } + + public static Bitmap drawableToBitmap (Drawable drawable) { + if (drawable == null) { + return null; + } else if (drawable instanceof BitmapDrawable) { + return ((BitmapDrawable) drawable).getBitmap(); + } + Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), + drawable.getIntrinsicHeight(), Config.ARGB_8888); + Canvas canvas = new Canvas(bitmap); + drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); + drawable.draw(canvas); + return bitmap; + } + + public static Bitmap getColoredBitmap(Drawable d, int color) { + if (d == null) { + return null; + } + Bitmap colorBitmap = ((BitmapDrawable) d).getBitmap(); + Bitmap grayscaleBitmap = toGrayscale(colorBitmap); + Paint pp = new Paint(); + pp.setAntiAlias(true); + PorterDuffColorFilter frontFilter = + new PorterDuffColorFilter(color, Mode.MULTIPLY); + pp.setColorFilter(frontFilter); + Canvas cc = new Canvas(grayscaleBitmap); + final Rect rect = new Rect(0, 0, grayscaleBitmap.getWidth(), grayscaleBitmap.getHeight()); + cc.drawBitmap(grayscaleBitmap, rect, rect, pp); + return grayscaleBitmap; + } + + public static Bitmap toGrayscale(Bitmap bmpOriginal) { + int width, height; + height = bmpOriginal.getHeight(); + width = bmpOriginal.getWidth(); + try { + bmpOriginal = RGB565toARGB888(bmpOriginal); + } catch (Exception e) { + e.printStackTrace(); + } + + Bitmap bmpGrayscale = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); + Canvas c = new Canvas(bmpGrayscale); + Paint paint = new Paint(); + paint.setAntiAlias(true); + ColorMatrix cm = new ColorMatrix(); + final Rect rect = new Rect(0, 0, width, height); + cm.setSaturation(0); + + ColorMatrixColorFilter f = new ColorMatrixColorFilter(cm); + paint.setColorFilter(f); + c.drawBitmap(bmpOriginal, rect, rect, paint); + return bmpGrayscale; + } + + public static int dpToPx(Context context, int dp) { + return (int) ((dp * context.getResources().getDisplayMetrics().density) + 0.5); + } + + public static Drawable resize(Context context, Drawable image, int size) { + if (image == null || context == null) { + return null; + } + if (image instanceof VectorDrawable) { + return image; + } else { + int newSize = dpToPx(context, size); + Bitmap bitmap = ((BitmapDrawable) image).getBitmap(); + Bitmap scaledBitmap = Bitmap.createBitmap(newSize, newSize, Config.ARGB_8888); + + float ratioX = newSize / (float) bitmap.getWidth(); + float ratioY = newSize / (float) bitmap.getHeight(); + float middleX = newSize / 2.0f; + float middleY = newSize / 2.0f; + + final Paint paint = new Paint(Paint.FILTER_BITMAP_FLAG); + paint.setAntiAlias(true); + + Matrix scaleMatrix = new Matrix(); + scaleMatrix.setScale(ratioX, ratioY, middleX, middleY); + + Canvas canvas = new Canvas(scaledBitmap); + canvas.setMatrix(scaleMatrix); + canvas.drawBitmap(bitmap, middleX - bitmap.getWidth() / 2, + middleY - bitmap.getHeight() / 2, paint); + return new BitmapDrawable(context.getResources(), scaledBitmap); + } + } + + public static Bitmap getRoundedCornerBitmap(Bitmap bitmap) { + if (bitmap == null) { + return null; + } + Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), + Config.ARGB_8888); + Canvas canvas = new Canvas(output); + + final int color = 0xff424242; + final Paint paint = new Paint(); + final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight()); + final RectF rectF = new RectF(rect); + final float roundPx = 24; + paint.setAntiAlias(true); + canvas.drawARGB(0, 0, 0, 0); + paint.setColor(color); + canvas.drawRoundRect(rectF, roundPx, roundPx, paint); + paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN)); + canvas.drawBitmap(bitmap, rect, rect, paint); + return output; + } + + public static Bitmap getCircleBitmap(Bitmap bitmap) { + if (bitmap == null) { + return null; + } + int width = bitmap.getWidth(); + int height = bitmap.getHeight(); + + Bitmap output = Bitmap.createBitmap(width, height, + Config.ARGB_8888); + Canvas canvas = new Canvas(output); + + BitmapShader shader = new BitmapShader(bitmap, TileMode.CLAMP, TileMode.CLAMP); + final Paint paint = new Paint(); + paint.setAntiAlias(true); + paint.setShader(shader); + + canvas.drawCircle(width/2, height/2, width/2, paint); + + return output; + } + + public static Drawable getVector(Resources res, int resId) { + return getVector(res, resId, 0, 0, false); + } + + public static Drawable getVector(Resources res, int resId, int width, int height) { + return getVector(res, resId, width, height, false); + } + + public static Drawable getVector(Resources res, int resId, boolean toBitmapDrawable) { + return getVector(res, resId, 0, 0, toBitmapDrawable); + } + + public static Drawable getVector(Resources res, int resId, int width, int height, + boolean toBitmapDrawable) { + if (width <= 0) { + width = VECTOR_WIDTH; + } + if (height <= 0) { + width = VECTOR_HEIGHT; + } + + VectorDrawable vectorDrawable = new VectorDrawable(); + vectorDrawable.setBounds(0, 0, width, height); + try { + XmlPullParser parser = res.getXml(resId); + AttributeSet attrs = Xml.asAttributeSet(parser); + + int type; + while ((type = parser.next()) != XmlPullParser.START_TAG && + type != XmlPullParser.END_DOCUMENT) { + // Empty loop + } + + if (type != XmlPullParser.START_TAG) { +// Log.e("ImageHelper VectorLoader", "No start tag found"); + } + + vectorDrawable.inflate(res, parser, attrs); + + if (!toBitmapDrawable) { + return vectorDrawable; + } + + return new BitmapDrawable(res, drawableToBitmap(vectorDrawable)); + } catch (Exception e) { +// Log.e("ImageHelper VectorLoader", "Error loading resource ID " + String.valueOf(resId) + " Try loading as a non vector"); + return null; + } + } + + /** + * @param context callers context + * @param uri Uri to handle + * @return A bitmap from the requested uri + * @throws IOException + * + * @Credit: StackOverflow + * http://stackoverflow.com/questions/35909008/pick-image + * -from-gallery-or-google-photos-failing + */ + public static Bitmap getBitmapFromUri(Context context, Uri uri) throws IOException { + if (context == null || uri == null) { + return null; + } + ParcelFileDescriptor parcelFileDescriptor = + context.getContentResolver().openFileDescriptor(uri, "r"); + FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor(); + Bitmap image = BitmapFactory.decodeFileDescriptor(fileDescriptor); + parcelFileDescriptor.close(); + return image; + } + + /** + * @param storageDir Desired location in storage as a File + * @param fileName Name of bitmap file to store + * @param bitmap the bitmap to store + * @return the Uri of the bitmap + */ + public static Uri addBitmapToStorage(File storageDir, String fileName, Bitmap bitmap) { + if (storageDir == null || fileName == null || bitmap == null) { + return null; + } + File imageFile = new File(storageDir, fileName); + try { + FileOutputStream fos = new FileOutputStream(imageFile); + bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos); + fos.flush(); + fos.close(); + } catch (Exception e) { + return null; + } + return Uri.fromFile(imageFile); + } + + public static Bitmap getBlurredImage(Context context, Bitmap image) { + return getBlurredImage(context, image, 3.5f); + } + + public static Bitmap getBlurredImage(Context context, Bitmap image, float radius) { + try { + image = RGB565toARGB888(image); + } catch (Exception e) { + e.printStackTrace(); + } + + Bitmap bitmap = Bitmap.createBitmap( + image.getWidth(), image.getHeight(), + Bitmap.Config.ARGB_8888); + RenderScript renderScript = RenderScript.create(context); + Allocation blurInput = Allocation.createFromBitmap(renderScript, image); + Allocation blurOutput = Allocation.createFromBitmap(renderScript, bitmap); + + ScriptIntrinsicBlur blur = ScriptIntrinsicBlur.create(renderScript, + Element.U8_4(renderScript)); + blur.setInput(blurInput); + blur.setRadius(radius); // radius must be 0 < r <= 25 + blur.forEach(blurOutput); + blurOutput.copyTo(bitmap); + renderScript.destroy(); + + return bitmap; + } + + public static Bitmap getGrayscaleBlurredImage(Context context, Bitmap image) { + return getGrayscaleBlurredImage(context, image, 3.5f); + } + + public static Bitmap getGrayscaleBlurredImage(Context context, Bitmap image, float radius) { + Bitmap finalImage = Bitmap.createBitmap( + image.getWidth(), image.getHeight(), + Bitmap.Config.ARGB_8888); + finalImage = toGrayscale(getBlurredImage(context, image, radius)); + return finalImage; + } + + private static Bitmap RGB565toARGB888(Bitmap img) throws Exception { + int numPixels = img.getWidth() * img.getHeight(); + int[] pixels = new int[numPixels]; + + //Get JPEG pixels. Each int is the color values for one pixel. + img.getPixels(pixels, 0, img.getWidth(), 0, 0, img.getWidth(), img.getHeight()); + + //Create a Bitmap of the appropriate format. + Bitmap result = Bitmap.createBitmap(img.getWidth(), img.getHeight(), Bitmap.Config.ARGB_8888); + + //Set RGB pixels. + result.setPixels(pixels, 0, result.getWidth(), 0, 0, result.getWidth(), result.getHeight()); + return result; + } + + public static Bitmap getCompressedBitmap(String imagePath) { + float maxHeight = 1920.0f; + float maxWidth = 1080.0f; + Bitmap scaledBitmap = null; + Bitmap bmp = null; + ByteArrayOutputStream out = null; + + try { + BitmapFactory.Options options = new BitmapFactory.Options(); + options.inJustDecodeBounds = true; + BitmapFactory.decodeFile(imagePath, options); + + int actualHeight = options.outHeight; + int actualWidth = options.outWidth; + float imgRatio = (float) actualWidth / (float) actualHeight; + float maxRatio = maxWidth / maxHeight; + + if (actualHeight > maxHeight || actualWidth > maxWidth) { + if (imgRatio < maxRatio) { + imgRatio = maxHeight / actualHeight; + actualWidth = (int) (imgRatio * actualWidth); + actualHeight = (int) maxHeight; + } else if (imgRatio > maxRatio) { + imgRatio = maxWidth / actualWidth; + actualHeight = (int) (imgRatio * actualHeight); + actualWidth = (int) maxWidth; + } else { + actualHeight = (int) maxHeight; + actualWidth = (int) maxWidth; + } + } + + options.inSampleSize = calculateInSampleSize(options, actualWidth, actualHeight); + options.inJustDecodeBounds = false; + options.inDither = false; + options.inPurgeable = true; + options.inInputShareable = true; + options.inTempStorage = new byte[16 * 1024]; + + bmp = BitmapFactory.decodeFile(imagePath, options); + if (bmp == null) return null; // Exit if the bitmap is null + + scaledBitmap = Bitmap.createBitmap(actualWidth, actualHeight, Bitmap.Config.ARGB_8888); + + float ratioX = actualWidth / (float) options.outWidth; + float ratioY = actualHeight / (float) options.outHeight; + float middleX = actualWidth / 2.0f; + float middleY = actualHeight / 2.0f; + + Matrix scaleMatrix = new Matrix(); + scaleMatrix.setScale(ratioX, ratioY, middleX, middleY); + + Canvas canvas = new Canvas(scaledBitmap); + canvas.setMatrix(scaleMatrix); + canvas.drawBitmap(bmp, middleX - bmp.getWidth() / 2, middleY - bmp.getHeight() / 2, new Paint(Paint.FILTER_BITMAP_FLAG)); + + ExifInterface exif = new ExifInterface(imagePath); + int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 0); + Matrix matrix = new Matrix(); + if (orientation == 6) { + matrix.postRotate(90); + } else if (orientation == 3) { + matrix.postRotate(180); + } else if (orientation == 8) { + matrix.postRotate(270); + } + scaledBitmap = Bitmap.createBitmap(scaledBitmap, 0, 0, scaledBitmap.getWidth(), scaledBitmap.getHeight(), matrix, true); + + out = new ByteArrayOutputStream(); + scaledBitmap.compress(Bitmap.CompressFormat.JPEG, 90, out); + + byte[] byteArray = out.toByteArray(); + + // Free resources + if (bmp != null) bmp.recycle(); + if (scaledBitmap != null && scaledBitmap != bmp) scaledBitmap.recycle(); + if (out != null) out.close(); + + return BitmapFactory.decodeByteArray(byteArray, 0, byteArray.length); + + } catch (IOException e) { + e.printStackTrace(); + } finally { + // Ensure all resources are released + if (bmp != null) bmp.recycle(); + if (scaledBitmap != null && scaledBitmap != bmp) scaledBitmap.recycle(); + if (out != null) { + try { + out.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + return null; + } + + private static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) { + final int height = options.outHeight; + final int width = options.outWidth; + int inSampleSize = 1; + + if (height > reqHeight || width > reqWidth) { + final int heightRatio = Math.round((float) height / (float) reqHeight); + final int widthRatio = Math.round((float) width / (float) reqWidth); + inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio; + } + final float totalPixels = width * height; + final float totalReqPixelsCap = reqWidth * reqHeight * 2; + + while (totalPixels / (inSampleSize * inSampleSize) > totalReqPixelsCap) { + inSampleSize++; + } + return inSampleSize; + } +} diff --git a/core/java/com/android/internal/util/crdroid/KeyProviderManager.java b/core/java/com/android/internal/util/crdroid/KeyProviderManager.java new file mode 100644 index 0000000000000..a5609eadadeb6 --- /dev/null +++ b/core/java/com/android/internal/util/crdroid/KeyProviderManager.java @@ -0,0 +1,191 @@ +/* + * SPDX-FileCopyrightText: 2024 Paranoid Android + * SPDX-License-Identifier: Apache-2.0 + */ +package com.android.internal.util.crdroid; + +import android.app.ActivityThread; +import android.content.Context; +import android.provider.Settings; +import android.util.Log; +import android.util.Xml; + +import org.xmlpull.v1.XmlPullParser; + +import java.io.StringReader; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Manager class for handling keybox providers. + * @hide + */ +public final class KeyProviderManager { + private static final String TAG = "KeyProviderManager"; + + private KeyProviderManager() {} + + public static IKeyboxProvider getProvider() { + return new DefaultKeyboxProvider(); + } + + public static boolean isKeyboxAvailable() { + return getProvider().hasKeybox(); + } + + private static class DefaultKeyboxProvider implements IKeyboxProvider { + private final Map keyboxData = new HashMap<>(); + + private DefaultKeyboxProvider() { + Context context = getApplicationContext(); + if (context == null) { + Log.e(TAG, "Failed to get application context"); + return; + } + + loadFromXmlSetting(context); + } + + private boolean loadFromXmlSetting(Context ctx) { + try { + String xml = Settings.Secure.getString(ctx.getContentResolver(), Settings.Secure.KEYBOX_DATA); + if (xml == null || xml.trim().isEmpty()) return false; + + XmlPullParser p = Xml.newPullParser(); + p.setInput(new StringReader(xml)); + + String currentAlg = null; + int certCount = 0; + boolean numberOfKeyboxesChecked = false; + + for (int ev = p.next(); ev != XmlPullParser.END_DOCUMENT; ev = p.next()) { + if (ev == XmlPullParser.START_TAG) { + String tag = p.getName(); + switch (tag) { + case "NumberOfKeyboxes": + p.next(); + numberOfKeyboxesChecked = true; + try { + int count = Integer.parseInt(p.getText().trim()); + if (count != 1) { + Log.w(TAG, "Invalid NumberOfKeyboxes: " + count); + return false; + } + } catch (NumberFormatException e) { + Log.w(TAG, "Failed to parse NumberOfKeyboxes", e); + return false; + } + break; + + case "Key": + currentAlg = p.getAttributeValue(null, "algorithm"); + if ("ecdsa".equalsIgnoreCase(currentAlg)) currentAlg = "EC"; + else if ("rsa".equalsIgnoreCase(currentAlg)) currentAlg = "RSA"; + else currentAlg = null; + certCount = 0; + break; + + case "PrivateKey": { + String format = p.getAttributeValue(null, "format"); + if (!"pem".equalsIgnoreCase(format)) { + Log.w(TAG, "Unsupported PrivateKey format: " + format); + return false; + } + p.next(); + if (currentAlg != null) { + keyboxData.put(currentAlg + ".PRIV", p.getText().trim()); + } + break; + } + + case "Certificate": { + String format = p.getAttributeValue(null, "format"); + if (!"pem".equalsIgnoreCase(format)) { + Log.w(TAG, "Unsupported Certificate format: " + format); + return false; + } + if (currentAlg != null) { + p.next(); + certCount++; + keyboxData.put(currentAlg + ".CERT_" + certCount, p.getText().trim()); + } + break; + } + } + } + } + + if (!numberOfKeyboxesChecked) { + Log.w(TAG, "Missing in keybox XML"); + return false; + } + + if (!hasKeybox()) { + Log.w(TAG, "Failed to load keybox from XML setting"); + return false; + } + + Log.i(TAG, "Loaded keybox from XML setting"); + return true; + } catch (Exception e) { + Log.e(TAG, "XML keybox load failed", e); + return false; + } + } + + private static Context getApplicationContext() { + try { + return ActivityThread.currentApplication().getApplicationContext(); + } catch (Exception e) { + Log.e(TAG, "Error getting application context", e); + return null; + } + } + + @Override + public boolean hasKeybox() { + if (!keyboxData.containsKey("EC.PRIV") || !keyboxData.containsKey("RSA.PRIV")) { + return false; + } + if (!keyboxData.containsKey("EC.CERT_1") || !keyboxData.containsKey("RSA.CERT_1")) { + return false; + } + return true; + } + + @Override + public String getEcPrivateKey() { + return keyboxData.get("EC.PRIV"); + } + + @Override + public String getRsaPrivateKey() { + return keyboxData.get("RSA.PRIV"); + } + + @Override + public String[] getEcCertificateChain() { + return getCertificateChain("EC"); + } + + @Override + public String[] getRsaCertificateChain() { + return getCertificateChain("RSA"); + } + + private String[] getCertificateChain(String prefix) { + List dataList = new ArrayList<>(); + for (String key : keyboxData.keySet()) { + if (key.startsWith(prefix + ".CERT_")) { + dataList.add(keyboxData.get(key)); + } + } + String[] chain = dataList.toArray(String[]::new); + Arrays.sort(chain); + return chain; + } + } +} diff --git a/core/java/com/android/internal/util/crdroid/KeyboxChainGenerator.java b/core/java/com/android/internal/util/crdroid/KeyboxChainGenerator.java new file mode 100644 index 0000000000000..1f760fbcf3883 --- /dev/null +++ b/core/java/com/android/internal/util/crdroid/KeyboxChainGenerator.java @@ -0,0 +1,483 @@ +/* + * SPDX-FileCopyrightText: 2025 Neoteric OS + * SPDX-FileCopyrightText: 2025 The halogenOS Project + * SPDX-License-Identifier: Apache-2.0 + */ +package com.android.internal.util.crdroid; + +import android.app.ActivityThread; +import android.content.Context; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.Signature; +import android.hardware.security.keymint.Algorithm; +import android.hardware.security.keymint.EcCurve; +import android.hardware.security.keymint.KeyOrigin; +import android.hardware.security.keymint.KeyParameter; +import android.hardware.security.keymint.Tag; +import android.os.Binder; +import android.os.Build; +import android.os.UserHandle; +import android.provider.Settings; +import android.security.keystore.KeyProperties; +import android.system.keystore2.KeyDescriptor; +import android.util.Base64; +import android.util.Log; + +import androidx.annotation.Nullable; + +import com.android.internal.org.bouncycastle.asn1.ASN1Boolean; +import com.android.internal.org.bouncycastle.asn1.ASN1Encodable; +import com.android.internal.org.bouncycastle.asn1.ASN1Enumerated; +import com.android.internal.org.bouncycastle.asn1.ASN1Integer; +import com.android.internal.org.bouncycastle.asn1.ASN1ObjectIdentifier; +import com.android.internal.org.bouncycastle.asn1.ASN1OctetString; +import com.android.internal.org.bouncycastle.asn1.ASN1Sequence; +import com.android.internal.org.bouncycastle.asn1.DERNull; +import com.android.internal.org.bouncycastle.asn1.DEROctetString; +import com.android.internal.org.bouncycastle.asn1.DERSequence; +import com.android.internal.org.bouncycastle.asn1.DERSet; +import com.android.internal.org.bouncycastle.asn1.DERTaggedObject; +import com.android.internal.org.bouncycastle.asn1.x500.X500Name; +import com.android.internal.org.bouncycastle.asn1.x509.Extension; +import com.android.internal.org.bouncycastle.asn1.x509.KeyUsage; +import com.android.internal.org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; +import com.android.internal.org.bouncycastle.asn1.x509.Time; +import com.android.internal.org.bouncycastle.cert.X509CertificateHolder; +import com.android.internal.org.bouncycastle.cert.X509v3CertificateBuilder; +import com.android.internal.org.bouncycastle.jce.provider.BouncyCastleProvider; +import com.android.internal.org.bouncycastle.operator.ContentSigner; +import com.android.internal.org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; + +import java.io.IOException; +import java.math.BigInteger; +import java.nio.charset.StandardCharsets; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.MessageDigest; +import java.security.SecureRandom; +import java.security.Security; +import java.security.cert.Certificate; +import java.security.spec.ECGenParameterSpec; +import java.security.spec.RSAKeyGenParameterSpec; +import java.util.Arrays; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashSet; +import java.util.List; +import java.util.Objects; +import java.util.Set; + +import javax.security.auth.x500.X500Principal; + +/** + * @hide + */ +public final class KeyboxChainGenerator { + + private static final String TAG = "KeyboxChainGenerator"; + private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + + private static final int ATTESTATION_APPLICATION_ID_PACKAGE_INFOS_INDEX = 0; + private static final int ATTESTATION_APPLICATION_ID_SIGNATURE_DIGESTS_INDEX = 1; + private static final int ATTESTATION_PACKAGE_INFO_PACKAGE_NAME_INDEX = 0; + private static final int ATTESTATION_PACKAGE_INFO_VERSION_INDEX = 1; + + public static List generateCertChain(int uid, KeyDescriptor descriptor, KeyGenParameters params) { + dlog("Requested KeyPair with alias: " + descriptor.alias); + int size = params.keySize; + KeyPair kp; + try { + if (Objects.equals(params.algorithm, Algorithm.EC)) { + dlog("Generating EC keypair of size " + size); + kp = buildECKeyPair(params); + } else if (Objects.equals(params.algorithm, Algorithm.RSA)) { + dlog("Generating RSA keypair of size " + size); + kp = buildRSAKeyPair(params); + } else { + dlog("Unsupported algorithm"); + return null; + } + + X509v3CertificateBuilder certBuilder = new X509v3CertificateBuilder( + KeyboxUtils.getCertificateHolder( + Objects.equals(params.algorithm, Algorithm.EC) + ? KeyProperties.KEY_ALGORITHM_EC + : KeyProperties.KEY_ALGORITHM_RSA + ).getSubject(), + params.certificateSerial, + new Time(params.certificateNotBefore), + new Time(params.certificateNotAfter), + params.certificateSubject, + SubjectPublicKeyInfo.getInstance( + ASN1Sequence.getInstance(kp.getPublic().getEncoded()) + ) + ); + + KeyUsage keyUsage = new KeyUsage(KeyUsage.keyCertSign); + certBuilder.addExtension(Extension.keyUsage, true, keyUsage); + certBuilder.addExtension(createExtension(params, uid)); + + ContentSigner contentSigner; + if (Objects.equals(params.algorithm, Algorithm.EC)) { + contentSigner = new JcaContentSignerBuilder("SHA256withECDSA").build(KeyboxUtils.getPrivateKey(KeyProperties.KEY_ALGORITHM_EC)); + } else { + contentSigner = new JcaContentSignerBuilder("SHA256withRSA").build(KeyboxUtils.getPrivateKey(KeyProperties.KEY_ALGORITHM_RSA)); + } + X509CertificateHolder certHolder = certBuilder.build(contentSigner); + Certificate leaf = KeyboxUtils.getCertificateFromHolder(certHolder); + List chain = KeyboxUtils.getCertificateChain(leaf.getPublicKey().getAlgorithm()); + chain.add(0, leaf); + dlog("Successfully generated X500 Cert for alias: " + descriptor.alias); + return chain; + } catch (Throwable t) { + Log.e(TAG, Log.getStackTraceString(t)); + } + return null; + } + + private static ASN1Encodable[] fromIntList(List list) { + ASN1Encodable[] result = new ASN1Encodable[list.size()]; + for (int i = 0; i < list.size(); i++) { + result[i] = new ASN1Integer(list.get(i)); + } + return result; + } + + private static Extension createExtension(KeyGenParameters params, int uid) { + try { + Context context = ActivityThread.currentApplication(); + if (context == null) { + Log.e(TAG, "Context is null in createExtension"); + return null; + } + SecureRandom secureRandom = new SecureRandom(); + + String key = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.VBOOT_KEY); + byte[] verifiedBootKey; + if (key == null) { + byte[] randomBytes = new byte[32]; + secureRandom.nextBytes(randomBytes); + String encoded = Base64.encodeToString(randomBytes, Base64.NO_WRAP); + Settings.Secure.putString(context.getContentResolver(), Settings.Secure.VBOOT_KEY, encoded); + verifiedBootKey = randomBytes; + } else { + verifiedBootKey = Base64.decode(key, Base64.NO_WRAP); + } + + String hash = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.VBOOT_HASH); + byte[] verifiedBootHash; + if (hash == null) { + byte[] randomBytes = new byte[32]; + secureRandom.nextBytes(randomBytes); + String encoded = Base64.encodeToString(randomBytes, Base64.NO_WRAP); + Settings.Secure.putString(context.getContentResolver(), Settings.Secure.VBOOT_HASH, encoded); + verifiedBootHash = randomBytes; + } else { + verifiedBootHash = Base64.decode(hash, Base64.NO_WRAP); + } + + ASN1Encodable[] rootOfTrustEncodables = { + new DEROctetString(verifiedBootKey), + ASN1Boolean.TRUE, + new ASN1Enumerated(0), + new DEROctetString(verifiedBootHash) + }; + + ASN1Sequence rootOfTrustSeq = new DERSequence(rootOfTrustEncodables); + + var Apurpose = new DERSet(fromIntList(params.purpose)); + var Aalgorithm = new ASN1Integer(params.algorithm); + var AkeySize = new ASN1Integer(params.keySize); + var Adigest = new DERSet(fromIntList(params.digest)); + var AecCurve = new ASN1Integer(params.ecCurve); + var AnoAuthRequired = DERNull.INSTANCE; + var Aorigin = new ASN1Integer(0); + + // To be loaded + var AosVersion = new ASN1Integer(getOsVersion()); + var AosPatchLevel = new ASN1Integer(getPatchLevel()); + var AbootPatchlevel = new ASN1Integer(getPatchLevelLong()); + var AvendorPatchLevel = new ASN1Integer(getPatchLevelLong()); + + var purpose = new DERTaggedObject(true, 1, Apurpose); + var algorithm = new DERTaggedObject(true, 2, Aalgorithm); + var keySize = new DERTaggedObject(true, 3, AkeySize); + var digest = new DERTaggedObject(true, 5, Adigest); + var ecCurve = new DERTaggedObject(true, 10, AecCurve); + var noAuthRequired = new DERTaggedObject(true, 503, AnoAuthRequired); + var origin = new DERTaggedObject(true, 702, Aorigin); + var rootOfTrust = new DERTaggedObject(true, 704, rootOfTrustSeq); + var osVersion = new DERTaggedObject(true, 705, AosVersion); + var osPatchLevel = new DERTaggedObject(true, 706, AosPatchLevel); + var vendorPatchLevel = new DERTaggedObject(true, 718, AvendorPatchLevel); + var bootPatchLevel = new DERTaggedObject(true, 719, AbootPatchlevel); + + ASN1Encodable[] teeEnforcedEncodables; + + // Support device properties attestation + if (params.brand != null) { + var Abrand = new DEROctetString(params.brand); + var Adevice = new DEROctetString(params.device); + var Aproduct = new DEROctetString(params.product); + var Amanufacturer = new DEROctetString(params.manufacturer); + var Amodel = new DEROctetString(params.model); + var brand = new DERTaggedObject(true, 710, Abrand); + var device = new DERTaggedObject(true, 711, Adevice); + var product = new DERTaggedObject(true, 712, Aproduct); + var manufacturer = new DERTaggedObject(true, 716, Amanufacturer); + var model = new DERTaggedObject(true, 717, Amodel); + + teeEnforcedEncodables = new ASN1Encodable[]{purpose, algorithm, keySize, digest, ecCurve, + noAuthRequired, origin, rootOfTrust, osVersion, osPatchLevel, vendorPatchLevel, + bootPatchLevel, brand, device, product, manufacturer, model}; + } else { + teeEnforcedEncodables = new ASN1Encodable[]{purpose, algorithm, keySize, digest, ecCurve, + noAuthRequired, origin, rootOfTrust, osVersion, osPatchLevel, vendorPatchLevel, + bootPatchLevel}; + } + + var AcreationDateTime = new ASN1Integer(System.currentTimeMillis()); + var AapplicationID = createApplicationId(uid); + + var creationDateTime = new DERTaggedObject(true, 701, AcreationDateTime); + var applicationID = new DERTaggedObject(true, 709, AapplicationID); + + ASN1Encodable[] softwareEnforced = {creationDateTime, applicationID}; + + ASN1OctetString keyDescriptionOctetStr = getAsn1OctetString(teeEnforcedEncodables, softwareEnforced, params); + + return new Extension(new ASN1ObjectIdentifier("1.3.6.1.4.1.11129.2.1.17"), false, keyDescriptionOctetStr); + } catch (Throwable t) { + Log.e(TAG, Log.getStackTraceString(t)); + } + return null; + } + + private static int getOsVersion() { + String release = Build.VERSION.RELEASE; + int major = 16, minor = 0, patch = 0; + + // Handle cases with additional suffixes like "/BP31" + release = release.split("/")[0]; + + String[] parts = release.split("\\."); + try { + if (parts.length > 0) major = Integer.parseInt(parts[0]); + if (parts.length > 1) minor = Integer.parseInt(parts[1]); + if (parts.length > 2) patch = Integer.parseInt(parts[2]); + } catch (NumberFormatException e) { + Log.w(TAG, "Unable to parse OS version: " + release); + } + + return major * 10000 + minor * 100 + patch; + } + + private static int getPatchLevel() { + return convertPatchLevel(Build.VERSION.SECURITY_PATCH, false); + } + + private static int getPatchLevelLong() { + return convertPatchLevel(Build.VERSION.SECURITY_PATCH, true); + } + + private static int convertPatchLevel(String patchLevel, boolean longFormat) { + try { + String[] parts = patchLevel.split("-"); + int year = Integer.parseInt(parts[0]); + int month = Integer.parseInt(parts[1]); + if (longFormat) { + int day = Integer.parseInt(parts[2]); + return year * 10000 + month * 100 + day; + } else { + return year * 100 + month; + } + } catch (Exception e) { + Log.e(TAG, "Invalid patch level: " + patchLevel, e); + return 202404; + } + } + + private static ASN1OctetString getAsn1OctetString(ASN1Encodable[] teeEnforcedEncodables, ASN1Encodable[] softwareEnforcedEncodables, KeyGenParameters params) throws IOException { + ASN1Integer attestationVersion = new ASN1Integer(100); + ASN1Enumerated attestationSecurityLevel = new ASN1Enumerated(1); + ASN1Integer keymasterVersion = new ASN1Integer(100); + ASN1Enumerated keymasterSecurityLevel = new ASN1Enumerated(1); + ASN1OctetString attestationChallenge = new DEROctetString(params.attestationChallenge); + ASN1OctetString uniqueId = new DEROctetString(new byte[0]); + ASN1Encodable softwareEnforced = new DERSequence(softwareEnforcedEncodables); + ASN1Sequence teeEnforced = new DERSequence(teeEnforcedEncodables); + + ASN1Encodable[] keyDescriptionEncodables = {attestationVersion, attestationSecurityLevel, keymasterVersion, + keymasterSecurityLevel, attestationChallenge, uniqueId, softwareEnforced, teeEnforced}; + + ASN1Sequence keyDescriptionHackSeq = new DERSequence(keyDescriptionEncodables); + + return new DEROctetString(keyDescriptionHackSeq); + } + + private static DEROctetString createApplicationId(int uid) throws Throwable { + Context context = ActivityThread.currentApplication(); + if (context == null) { + throw new IllegalStateException("createApplicationId: context not available from ActivityThread!"); + } + + PackageManager pm = context.getPackageManager(); + if (pm == null) { + throw new IllegalStateException("createApplicationId: PackageManager not found!"); + } + + String[] packages = pm.getPackagesForUid(uid); + if (packages == null || packages.length == 0) { + throw new IllegalStateException("No packages found for UID: " + uid); + } + + int size = packages.length; + ASN1Encodable[] packageInfoAA = new ASN1Encodable[size]; + Set signatures = new HashSet<>(); + MessageDigest dg = MessageDigest.getInstance("SHA-256"); + + for (int i = 0; i < size; i++) { + String name = packages[i]; + PackageInfo info = pm.getPackageInfo(name, PackageManager.GET_SIGNATURES); + ASN1Encodable[] arr = new ASN1Encodable[2]; + arr[ATTESTATION_PACKAGE_INFO_PACKAGE_NAME_INDEX] = + new DEROctetString(name.getBytes(StandardCharsets.UTF_8)); + arr[ATTESTATION_PACKAGE_INFO_VERSION_INDEX] = + new ASN1Integer(info.getLongVersionCode()); + packageInfoAA[i] = new DERSequence(arr); + + if (info != null && info.signatures != null) { + for (Signature s : info.signatures) { + if (s != null) signatures.add(new Digest(dg.digest(s.toByteArray()))); + } + } + } + + ASN1Encodable[] signaturesAA = new ASN1Encodable[signatures.size()]; + int i = 0; + for (Digest d : signatures) { + signaturesAA[i++] = new DEROctetString(d.digest); + } + + ASN1Encodable[] applicationIdAA = new ASN1Encodable[2]; + applicationIdAA[ATTESTATION_APPLICATION_ID_PACKAGE_INFOS_INDEX] = + new DERSet(packageInfoAA); + applicationIdAA[ATTESTATION_APPLICATION_ID_SIGNATURE_DIGESTS_INDEX] = + new DERSet(signaturesAA); + + return new DEROctetString(new DERSequence(applicationIdAA).getEncoded()); + } + + record Digest(byte[] digest) { + @Override + public boolean equals(@Nullable Object o) { + if (o instanceof Digest d) + return Arrays.equals(digest, d.digest); + return false; + } + + @Override + public int hashCode() { + return Arrays.hashCode(digest); + } + } + + private static KeyPair buildECKeyPair(KeyGenParameters params) throws Exception { + Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME); + Security.addProvider(new BouncyCastleProvider()); + ECGenParameterSpec spec = new ECGenParameterSpec(params.ecCurveName); + KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC", BouncyCastleProvider.PROVIDER_NAME); + kpg.initialize(spec); + return kpg.generateKeyPair(); + } + + private static KeyPair buildRSAKeyPair(KeyGenParameters params) throws Exception { + Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME); + Security.addProvider(new BouncyCastleProvider()); + RSAKeyGenParameterSpec spec = new RSAKeyGenParameterSpec( + params.keySize, params.rsaPublicExponent); + KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA", BouncyCastleProvider.PROVIDER_NAME); + kpg.initialize(spec); + return kpg.generateKeyPair(); + } + + private static void dlog(String msg) { + if (DEBUG) Log.d(TAG, msg); + } + + public static class KeyGenParameters { + public int keySize; + public int algorithm; + public BigInteger certificateSerial; + public Date certificateNotBefore; + public Date certificateNotAfter; + public X500Name certificateSubject; + + public BigInteger rsaPublicExponent; + public int ecCurve; + public String ecCurveName; + + public List purpose = new ArrayList<>(); + public List digest = new ArrayList<>(); + + public byte[] attestationChallenge; + public byte[] brand; + public byte[] device; + public byte[] product; + public byte[] manufacturer; + public byte[] model; + + public int securityLevel; + public boolean noAuthRequired; + + // Extra fields for response metadata + public int osVersion = KeyboxChainGenerator.getOsVersion(); + public int osPatchLevel = KeyboxChainGenerator.getPatchLevel(); + public int vendorPatchLevel = KeyboxChainGenerator.getPatchLevelLong(); + public int bootPatchLevel = KeyboxChainGenerator.getPatchLevelLong(); + public long creationDateTime = System.currentTimeMillis(); + public int userId = UserHandle.myUserId(); + public int origin = KeyOrigin.GENERATED; + + public KeyGenParameters(KeyParameter[] params) { + for (KeyParameter kp : params) { + switch (kp.tag) { + case Tag.KEY_SIZE -> keySize = kp.value.getInteger(); + case Tag.ALGORITHM -> algorithm = kp.value.getAlgorithm(); + case Tag.CERTIFICATE_SERIAL -> certificateSerial = new BigInteger(kp.value.getBlob()); + case Tag.CERTIFICATE_NOT_BEFORE -> certificateNotBefore = new Date(kp.value.getDateTime()); + case Tag.CERTIFICATE_NOT_AFTER -> certificateNotAfter = new Date(kp.value.getDateTime()); + case Tag.CERTIFICATE_SUBJECT -> + certificateSubject = new X500Name(new X500Principal(kp.value.getBlob()).getName()); + case Tag.RSA_PUBLIC_EXPONENT -> rsaPublicExponent = BigInteger.valueOf(kp.value.getLongInteger()); + case Tag.EC_CURVE -> { + ecCurve = kp.value.getEcCurve(); + ecCurveName = getEcCurveName(ecCurve); + } + case Tag.PURPOSE -> purpose.add(kp.value.getKeyPurpose()); + case Tag.DIGEST -> digest.add(kp.value.getDigest()); + case Tag.ATTESTATION_CHALLENGE -> attestationChallenge = kp.value.getBlob(); + case Tag.ATTESTATION_ID_BRAND -> brand = kp.value.getBlob(); + case Tag.ATTESTATION_ID_DEVICE -> device = kp.value.getBlob(); + case Tag.ATTESTATION_ID_PRODUCT -> product = kp.value.getBlob(); + case Tag.ATTESTATION_ID_MANUFACTURER -> manufacturer = kp.value.getBlob(); + case Tag.ATTESTATION_ID_MODEL -> model = kp.value.getBlob(); + case Tag.HARDWARE_TYPE -> securityLevel = kp.value.getSecurityLevel(); + case Tag.NO_AUTH_REQUIRED -> noAuthRequired = kp.value.getBoolValue(); + } + } + } + + private static String getEcCurveName(int curve) { + return switch (curve) { + case EcCurve.CURVE_25519 -> "CURVE_25519"; + case EcCurve.P_224 -> "secp224r1"; + case EcCurve.P_256 -> "secp256r1"; + case EcCurve.P_384 -> "secp384r1"; + case EcCurve.P_521 -> "secp521r1"; + default -> throw new IllegalArgumentException("unknown curve"); + }; + } + } +} diff --git a/core/java/com/android/internal/util/crdroid/KeyboxImitationHooks.java b/core/java/com/android/internal/util/crdroid/KeyboxImitationHooks.java new file mode 100644 index 0000000000000..55b235a1cd46e --- /dev/null +++ b/core/java/com/android/internal/util/crdroid/KeyboxImitationHooks.java @@ -0,0 +1,224 @@ +/* + * SPDX-FileCopyrightText: 2024 Paranoid Android + * SPDX-FileCopyrightText: 2025 Neoteric OS + * SPDX-License-Identifier: Apache-2.0 + */ +package com.android.internal.util.crdroid; + +import android.hardware.security.keymint.Algorithm; +import android.hardware.security.keymint.KeyOrigin; +import android.hardware.security.keymint.KeyParameter; +import android.hardware.security.keymint.KeyParameterValue; +import android.hardware.security.keymint.KeyPurpose; +import android.hardware.security.keymint.Tag; +import android.os.Binder; +import android.system.keystore2.Authorization; +import android.system.keystore2.IKeystoreSecurityLevel; +import android.system.keystore2.KeyDescriptor; +import android.system.keystore2.KeyEntryResponse; +import android.system.keystore2.KeyMetadata; +import android.util.Log; + +import com.android.internal.util.crdroid.KeyboxChainGenerator.KeyGenParameters; + +import java.security.cert.Certificate; +import java.util.ArrayList; +import java.util.Collection; +import java.util.LinkedList; +import java.util.List; + +/** + * @hide + */ +public class KeyboxImitationHooks { + + private static final String TAG = "KeyboxImitationHooks"; + private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + + public static KeyEntryResponse onGetKeyEntry(KeyDescriptor descriptor) { + if (!KeyProviderManager.isKeyboxAvailable()) { + return null; + } + + KeyEntryResponse spoofed = KeyboxUtils.retrieve(Binder.getCallingUid(), descriptor.alias); + if (spoofed != null) { + dlog("Key entry spoofed"); + return spoofed; + } + + return null; + } + + public static KeyMetadata generateKey(IKeystoreSecurityLevel level, KeyDescriptor descriptor, Collection args) { + if (!KeyProviderManager.isKeyboxAvailable()) { + return null; + } + + KeyGenParameters params = new KeyGenParameters(args.toArray(new KeyParameter[args.size()])); + + if (params.attestationChallenge == null) { + return null; + } + + if (params.purpose == null || !params.purpose.contains(KeyPurpose.SIGN)) { + return null; + } + + if (!params.noAuthRequired) { + return null; + } + + if (params.algorithm != Algorithm.EC && params.algorithm != Algorithm.RSA) { + Log.w(TAG, "Unsupported algorithm: " + params.algorithm); + return null; + } + + int uid = Binder.getCallingUid(); + try { + List chain = KeyboxChainGenerator.generateCertChain(uid, descriptor, params); + if (chain == null || chain.isEmpty()) { + return null; + } + KeyEntryResponse response = buildResponse(level, chain, params, descriptor); + if (response == null) { + return null; + } + KeyboxUtils.append(uid, descriptor.alias, response); + return response.metadata; + } catch (Exception e) { + Log.e(TAG, "Failed to generate key", e); + return null; + } + } + + private static KeyEntryResponse buildResponse( + IKeystoreSecurityLevel level, + List chain, + KeyGenParameters params, + KeyDescriptor descriptor + ) { + try { + KeyEntryResponse response = new KeyEntryResponse(); + KeyMetadata metadata = new KeyMetadata(); + metadata.keySecurityLevel = params.securityLevel; + + KeyboxUtils.putCertificateChain(metadata, chain.toArray(new Certificate[chain.size()])); + + KeyDescriptor d = new KeyDescriptor(); + d.domain = descriptor.domain; + d.nspace = descriptor.nspace; + metadata.key = d; + + List authorizations = new ArrayList<>(); + Authorization a; + + for (Integer i : params.purpose) { + a = new Authorization(); + a.keyParameter = new KeyParameter(); + a.keyParameter.tag = Tag.PURPOSE; + a.keyParameter.value = KeyParameterValue.keyPurpose(i); + a.securityLevel = params.securityLevel; + authorizations.add(a); + } + + for (Integer i : params.digest) { + a = new Authorization(); + a.keyParameter = new KeyParameter(); + a.keyParameter.tag = Tag.DIGEST; + a.keyParameter.value = KeyParameterValue.digest(i); + a.securityLevel = params.securityLevel; + authorizations.add(a); + } + + a = new Authorization(); + a.keyParameter = new KeyParameter(); + a.keyParameter.tag = Tag.ALGORITHM; + a.keyParameter.value = KeyParameterValue.algorithm(params.algorithm); + a.securityLevel = params.securityLevel; + authorizations.add(a); + + a = new Authorization(); + a.keyParameter = new KeyParameter(); + a.keyParameter.tag = Tag.KEY_SIZE; + a.keyParameter.value = KeyParameterValue.integer(params.keySize); + a.securityLevel = params.securityLevel; + authorizations.add(a); + + a = new Authorization(); + a.keyParameter = new KeyParameter(); + a.keyParameter.tag = Tag.EC_CURVE; + a.keyParameter.value = KeyParameterValue.ecCurve(params.ecCurve); + a.securityLevel = params.securityLevel; + authorizations.add(a); + + a = new Authorization(); + a.keyParameter = new KeyParameter(); + a.keyParameter.tag = Tag.NO_AUTH_REQUIRED; + a.keyParameter.value = KeyParameterValue.boolValue(true); + a.securityLevel = params.securityLevel; + authorizations.add(a); + + a = new Authorization(); + a.keyParameter = new KeyParameter(); + a.keyParameter.tag = Tag.ORIGIN; + a.keyParameter.value = KeyParameterValue.origin(params.origin); + a.securityLevel = params.securityLevel; + authorizations.add(a); + + a = new Authorization(); + a.keyParameter = new KeyParameter(); + a.keyParameter.tag = Tag.OS_VERSION; + a.keyParameter.value = KeyParameterValue.integer(params.osVersion); + a.securityLevel = params.securityLevel; + authorizations.add(a); + + a = new Authorization(); + a.keyParameter = new KeyParameter(); + a.keyParameter.tag = Tag.OS_PATCHLEVEL; + a.keyParameter.value = KeyParameterValue.integer(params.osPatchLevel); + a.securityLevel = params.securityLevel; + authorizations.add(a); + + a = new Authorization(); + a.keyParameter = new KeyParameter(); + a.keyParameter.tag = Tag.VENDOR_PATCHLEVEL; + a.keyParameter.value = KeyParameterValue.integer(params.vendorPatchLevel); + a.securityLevel = params.securityLevel; + authorizations.add(a); + + a = new Authorization(); + a.keyParameter = new KeyParameter(); + a.keyParameter.tag = Tag.BOOT_PATCHLEVEL; + a.keyParameter.value = KeyParameterValue.integer(params.bootPatchLevel); + a.securityLevel = params.securityLevel; + authorizations.add(a); + + a = new Authorization(); + a.keyParameter = new KeyParameter(); + a.keyParameter.tag = Tag.CREATION_DATETIME; + a.keyParameter.value = KeyParameterValue.longInteger(params.creationDateTime); + a.securityLevel = params.securityLevel; + authorizations.add(a); + + a = new Authorization(); + a.keyParameter = new KeyParameter(); + a.keyParameter.tag = Tag.USER_ID; + a.keyParameter.value = KeyParameterValue.integer(params.userId); + a.securityLevel = params.securityLevel; + authorizations.add(a); + + metadata.authorizations = authorizations.toArray(new Authorization[0]); + metadata.modificationTimeMs = System.currentTimeMillis(); + response.metadata = metadata; + response.iSecurityLevel = level; + return response; + } catch (Exception e) { + Log.e(TAG, "Failed to build key entry response", e); + return null; + } + } + + private static void dlog(String msg) { + if (DEBUG) Log.d(TAG, msg); + } +} diff --git a/core/java/com/android/internal/util/crdroid/KeyboxUtils.java b/core/java/com/android/internal/util/crdroid/KeyboxUtils.java new file mode 100644 index 0000000000000..bb4205bdeaa18 --- /dev/null +++ b/core/java/com/android/internal/util/crdroid/KeyboxUtils.java @@ -0,0 +1,158 @@ +/* + * SPDX-FileCopyrightText: 2025 Neoteric OS + * SPDX-License-Identifier: Apache-2.0 + */ +package com.android.internal.util.crdroid; + +import android.security.keystore.KeyProperties; +import android.system.keystore2.KeyEntryResponse; +import android.system.keystore2.KeyMetadata; + +import com.android.internal.org.bouncycastle.asn1.ASN1Sequence; +import com.android.internal.org.bouncycastle.asn1.ASN1Primitive; +import com.android.internal.org.bouncycastle.asn1.DERNull; +import com.android.internal.org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; +import com.android.internal.org.bouncycastle.asn1.pkcs.PrivateKeyInfo; +import com.android.internal.org.bouncycastle.asn1.pkcs.RSAPrivateKey; +import com.android.internal.org.bouncycastle.asn1.sec.ECPrivateKey; +import com.android.internal.org.bouncycastle.asn1.x9.X9ObjectIdentifiers; +import com.android.internal.org.bouncycastle.asn1.x509.AlgorithmIdentifier; +import com.android.internal.org.bouncycastle.cert.X509CertificateHolder; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.security.KeyFactory; +import java.security.PrivateKey; +import java.security.cert.Certificate; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.RSAPrivateCrtKeySpec; +import java.util.ArrayList; +import java.util.Base64; +import java.util.List; +import java.util.concurrent.ConcurrentHashMap; + +/** + * @hide + */ +public class KeyboxUtils { + + private static final ConcurrentHashMap response = new ConcurrentHashMap<>(); + public static record Key(int uid, String alias) {} + + public static byte[] decodePemOrBase64(String input) { + String base64 = input + .replaceAll("-----BEGIN [^-]+-----", "") + .replaceAll("-----END [^-]+-----", "") + .replaceAll("\\s+", ""); + return Base64.getDecoder().decode(base64); + } + + public static PrivateKey parsePrivateKey(String encodedKey, String algorithm) throws Exception { + byte[] keyBytes = decodePemOrBase64(encodedKey); + ASN1Primitive primitive = ASN1Primitive.fromByteArray(keyBytes); + if ("EC".equalsIgnoreCase(algorithm)) { + try { + // Try parsing as PKCS#8 + PrivateKeyInfo info = PrivateKeyInfo.getInstance(primitive); + return KeyFactory.getInstance("EC").generatePrivate(new PKCS8EncodedKeySpec(info.getEncoded())); + } catch (Exception e) { + // Possibly SEC1 / PKCS#1 EC + ASN1Sequence seq = ASN1Sequence.getInstance(primitive); + ECPrivateKey ecPrivateKey = ECPrivateKey.getInstance(seq); + AlgorithmIdentifier algId = new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, ecPrivateKey.getParameters()); + PrivateKeyInfo privInfo = new PrivateKeyInfo(algId, ecPrivateKey); + PKCS8EncodedKeySpec pkcs8Spec = new PKCS8EncodedKeySpec(privInfo.getEncoded()); + return KeyFactory.getInstance("EC").generatePrivate(pkcs8Spec); + } + } else if ("RSA".equalsIgnoreCase(algorithm)) { + try { + // Try parsing as PKCS#8 + return KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(keyBytes)); + } catch (Exception e) { + // Parse as PKCS#1 + RSAPrivateKey rsaKey = RSAPrivateKey.getInstance(primitive); + AlgorithmIdentifier algId = new AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption, DERNull.INSTANCE); + PrivateKeyInfo privInfo = new PrivateKeyInfo(algId, rsaKey); + PKCS8EncodedKeySpec pkcs8Spec = new PKCS8EncodedKeySpec(privInfo.getEncoded()); + return KeyFactory.getInstance("RSA").generatePrivate(pkcs8Spec); + } + } else { + throw new IllegalArgumentException("Unsupported algorithm: " + algorithm); + } + } + + public static X509Certificate parseCertificate(String encodedCert) throws Exception { + byte[] certBytes = decodePemOrBase64(encodedCert); + return (X509Certificate) CertificateFactory + .getInstance("X.509") + .generateCertificate(new ByteArrayInputStream(certBytes)); + } + + public static List getCertificateChain(String algorithm) throws Exception { + IKeyboxProvider provider = KeyProviderManager.getProvider(); + String[] certChainPem = KeyProperties.KEY_ALGORITHM_EC.equals(algorithm) + ? provider.getEcCertificateChain() + : provider.getRsaCertificateChain(); + + CertificateFactory factory = CertificateFactory.getInstance("X.509"); + List certs = new ArrayList<>(); + + for (String certPem : certChainPem) { + certs.add(parseCertificate(certPem)); + } + + return certs; + } + + public static void putCertificateChain(KeyEntryResponse response, Certificate[] chain) throws Exception { + putCertificateChain(response.metadata, chain); + } + + public static void putCertificateChain(KeyMetadata metadata, Certificate[] chain) throws Exception { + metadata.certificate = chain[0].getEncoded(); + var output = new ByteArrayOutputStream(); + for (int i = 1; i < chain.length; i++) { + output.write(chain[i].getEncoded()); + } + metadata.certificateChain = output.toByteArray(); + } + + public static X509Certificate getCertificateFromHolder(X509CertificateHolder holder) throws Exception { + CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); + ByteArrayInputStream in = new ByteArrayInputStream(holder.getEncoded()); + return (X509Certificate) certFactory.generateCertificate(in); + } + + public static PrivateKey getPrivateKey(String algorithm) throws Exception { + IKeyboxProvider provider = KeyProviderManager.getProvider(); + String privateKeyEncoded = KeyProperties.KEY_ALGORITHM_EC.equals(algorithm) + ? provider.getEcPrivateKey() + : provider.getRsaPrivateKey(); + + return parsePrivateKey(privateKeyEncoded, algorithm); + } + + public static X509CertificateHolder getCertificateHolder(String algorithm) throws Exception { + IKeyboxProvider provider = KeyProviderManager.getProvider(); + String certPem = KeyProperties.KEY_ALGORITHM_EC.equals(algorithm) + ? provider.getEcCertificateChain()[0] + : provider.getRsaCertificateChain()[0]; + + X509Certificate parsedCert = parseCertificate(certPem); + return new X509CertificateHolder(parsedCert.getEncoded()); + } + + public static void append(int uid, String a, KeyEntryResponse c) { + response.put(new Key(uid, a), c); + } + + public static void remove(int uid, String a) { + response.remove(new Key(uid, a)); + } + + public static KeyEntryResponse retrieve(int uid, String a) { + return response.get(new Key(uid, a)); + } +} diff --git a/core/java/com/android/internal/util/crdroid/OmniJawsClient.java b/core/java/com/android/internal/util/crdroid/OmniJawsClient.java new file mode 100644 index 0000000000000..f0ad0729c2a8f --- /dev/null +++ b/core/java/com/android/internal/util/crdroid/OmniJawsClient.java @@ -0,0 +1,407 @@ +/* + * Copyright (C) 2021 The OmniROM project + * Copyright (C) 2022-2025 crDroid Android project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.util.crdroid; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.res.Resources; +import android.database.Cursor; +import android.graphics.Color; +import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.text.TextUtils; +import android.util.Log; + +import java.lang.ref.WeakReference; +import java.text.DecimalFormat; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; + +public class OmniJawsClient { + + private static final String TAG = "OmniJawsClient"; + private static final boolean DEBUG = false; + + public static final String SERVICE_PACKAGE = "org.omnirom.omnijaws"; + public static final Uri WEATHER_URI = Uri.parse("content://org.omnirom.omnijaws.provider/weather"); + public static final Uri SETTINGS_URI = Uri.parse("content://org.omnirom.omnijaws.provider/settings"); + public static final String WEATHER_UPDATE = SERVICE_PACKAGE + ".WEATHER_UPDATE"; + public static final String WEATHER_ERROR = SERVICE_PACKAGE + ".WEATHER_ERROR"; + + private static final String ICON_PACKAGE_DEFAULT = "org.omnirom.omnijaws"; + private static final String ICON_PREFIX_DEFAULT = "google_new_light"; + private static final String EXTRA_ERROR = "error"; + public static final int EXTRA_ERROR_NETWORK = 0; + public static final int EXTRA_ERROR_LOCATION = 1; + public static final int EXTRA_ERROR_DISABLED = 2; + + public static final String[] WEATHER_PROJECTION = { + "city", "wind_speed", "wind_direction", "condition_code", "temperature", + "humidity", "condition", "forecast_low", "forecast_high", "forecast_condition", + "forecast_condition_code", "time_stamp", "forecast_date", "pin_wheel" + }; + + public static final String[] SETTINGS_PROJECTION = { + "enabled", "units", "provider", "setup", "icon_pack" + }; + + private static final DecimalFormat sNoDigitsFormat = new DecimalFormat("0"); + + private static OmniJawsClient sInstance; + + private WeatherInfo mCachedInfo; + private Resources mRes; + private String mPackageName; + private String mIconPrefix; + private String mSettingIconPackage; + private boolean mMetric; + + private final List> mObservers = new ArrayList<>(); + private WeatherUpdateReceiver mReceiver; + private boolean mWeatherReceiverRegistered = false; + + public static OmniJawsClient get() { + if (sInstance == null) { + synchronized (OmniJawsClient.class) { + if (sInstance == null) { + sInstance = new OmniJawsClient(); + } + } + } + return sInstance; + } + + public interface OmniJawsObserver { + void weatherUpdated(); + void weatherError(int errorReason); + default void updateSettings() {} + } + + private class WeatherUpdateReceiver extends BroadcastReceiver { + @Override public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + pruneDeadObservers(); + for (WeakReference ref : mObservers) { + OmniJawsObserver obs = ref.get(); + if (obs == null) continue; + if (WEATHER_UPDATE.equals(action)) { + obs.weatherUpdated(); + } else if (WEATHER_ERROR.equals(action)) { + obs.weatherError(intent.getIntExtra(EXTRA_ERROR, 0)); + } + } + } + } + + public Intent getSettingsIntent() { + return new Intent(Intent.ACTION_MAIN) + .setClassName(SERVICE_PACKAGE, SERVICE_PACKAGE + ".SettingsActivity"); + } + + public Intent getWeatherActivityIntent(Context context) { + if (isOmniJawsEnabled(context)) { + return new Intent(Intent.ACTION_MAIN) + .setClassName(SERVICE_PACKAGE, SERVICE_PACKAGE + ".WeatherActivity"); + } + return getSettingsIntent(); + } + + public WeatherInfo getWeatherInfo() { + return mCachedInfo; + } + + public void queryWeather(Context context) { + if (!isOmniJawsEnabled(context)) { + Log.w(TAG, "queryWeather while disabled"); + mCachedInfo = null; + return; + } + + try (Cursor weatherCursor = context.getContentResolver().query( + WEATHER_URI, WEATHER_PROJECTION, null, null, null)) { + + if (weatherCursor != null && weatherCursor.getCount() > 0) { + mCachedInfo = new WeatherInfo(); + List forecasts = new ArrayList<>(); + + for (int i = 0; i < weatherCursor.getCount(); i++) { + weatherCursor.moveToPosition(i); + if (i == 0) { + mCachedInfo.city = weatherCursor.getString(0); + mCachedInfo.windSpeed = getFormattedValue(weatherCursor.getFloat(1)); + mCachedInfo.windDirection = weatherCursor.getInt(2) + "\u00b0"; + mCachedInfo.conditionCode = weatherCursor.getInt(3); + mCachedInfo.temp = getFormattedValue(weatherCursor.getFloat(4)); + mCachedInfo.humidity = weatherCursor.getString(5); + mCachedInfo.condition = weatherCursor.getString(6); + mCachedInfo.timeStamp = Long.parseLong(weatherCursor.getString(11)); + mCachedInfo.pinWheel = weatherCursor.getString(13); + } else { + DayForecast day = new DayForecast(); + day.low = getFormattedValue(weatherCursor.getFloat(7)); + day.high = getFormattedValue(weatherCursor.getFloat(8)); + day.condition = weatherCursor.getString(9); + day.conditionCode = weatherCursor.getInt(10); + day.date = weatherCursor.getString(12); + forecasts.add(day); + } + } + mCachedInfo.forecasts = forecasts; + } + } catch (Exception e) { + Log.e(TAG, "queryWeather: weather", e); + } + + try (Cursor settingsCursor = context.getContentResolver().query( + SETTINGS_URI, SETTINGS_PROJECTION, null, null, null)) { + + if (settingsCursor != null && settingsCursor.moveToFirst()) { + mMetric = settingsCursor.getInt(1) == 0; + if (mCachedInfo != null) { + mCachedInfo.tempUnits = getTemperatureUnit(); + mCachedInfo.windUnits = getWindUnit(); + mCachedInfo.provider = settingsCursor.getString(2); + mCachedInfo.iconPack = settingsCursor.getString(4); + } + } + } catch (Exception e) { + Log.e(TAG, "queryWeather: settings", e); + } + + updateSettings(context); + } + + private void updateSettings(Context context) { + String iconPack = (mCachedInfo != null) ? mCachedInfo.iconPack : null; + if (iconPack == null || TextUtils.isEmpty(iconPack)) { + loadDefaultIconsPackage(context); + } else if (!iconPack.equals(mSettingIconPackage)) { + mSettingIconPackage = iconPack; + loadCustomIconPackage(context); + } + } + + private void loadDefaultIconsPackage(Context context) { + mPackageName = ICON_PACKAGE_DEFAULT; + mIconPrefix = ICON_PREFIX_DEFAULT; + mSettingIconPackage = mPackageName + "." + mIconPrefix; + try { + mRes = context.getPackageManager().getResourcesForApplication(mPackageName); + } catch (Exception e) { + Log.w(TAG, "No default icon package found"); + mRes = null; + } + } + + private void loadCustomIconPackage(Context context) { + int idx = mSettingIconPackage.lastIndexOf("."); + if (idx == -1) { + loadDefaultIconsPackage(context); + return; + } + mPackageName = mSettingIconPackage.substring(0, idx); + mIconPrefix = mSettingIconPackage.substring(idx + 1); + try { + mRes = context.getPackageManager().getResourcesForApplication(mPackageName); + } catch (Exception e) { + Log.w(TAG, "Icon pack loading failed, fallback to default"); + loadDefaultIconsPackage(context); + } + } + + private static String getFormattedValue(float value) { + if (Float.isNaN(value)) return "-"; + String result = sNoDigitsFormat.format(value); + return result.equals("-0") ? "0" : result; + } + + public boolean isOmniJawsServiceInstalled(Context context) { + return isAvailableApp(context, SERVICE_PACKAGE); + } + + public boolean isOmniJawsEnabled(Context context) { + if (!isOmniJawsServiceInstalled(context)) return false; + + try (Cursor c = context.getContentResolver().query( + SETTINGS_URI, SETTINGS_PROJECTION, null, null, null)) { + return c != null && c.moveToFirst() && c.getInt(0) == 1; + } catch (Exception e) { + Log.e(TAG, "isOmniJawsEnabled", e); + return false; + } + } + + private boolean isAvailableApp(Context context, String pkg) { + try { + PackageManager pm = context.getPackageManager(); + pm.getPackageInfo(pkg, PackageManager.GET_ACTIVITIES); + int state = pm.getApplicationEnabledSetting(pkg); + return state != PackageManager.COMPONENT_ENABLED_STATE_DISABLED + && state != PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER; + } catch (NameNotFoundException | IllegalArgumentException e) { + return false; + } + } + + public void addObserver(Context context, OmniJawsObserver observer) { + if (observer == null) return; + removeObserver(context, observer); + mObservers.add(new WeakReference<>(observer)); + registerReceiverIfNeeded(context); + } + + public void removeObserver(Context context, OmniJawsObserver observer) { + if (observer == null) return; + Iterator> it = mObservers.iterator(); + while (it.hasNext()) { + OmniJawsObserver o = it.next().get(); + if (o == null || o == observer) { + it.remove(); + } + } + if (mObservers.isEmpty()) { + unregisterReceiver(context); + } + } + + private void pruneDeadObservers() { + try { + mObservers.removeIf(ref -> ref.get() == null); + } catch (Exception e) { + Log.w(TAG, "Exception occured while pruning, ignoring"); + } + } + + private void registerReceiverIfNeeded(Context context) { + if (!mWeatherReceiverRegistered && !mObservers.isEmpty()) { + if (mReceiver != null) { + try { + context.unregisterReceiver(mReceiver); + } catch (Exception ignored) {} + } + mReceiver = new WeatherUpdateReceiver(); + IntentFilter filter = new IntentFilter(); + filter.addAction(WEATHER_UPDATE); + filter.addAction(WEATHER_ERROR); + context.registerReceiver(mReceiver, filter, Context.RECEIVER_EXPORTED); + mWeatherReceiverRegistered = true; + } + } + + private void unregisterReceiver(Context context) { + if (mWeatherReceiverRegistered && mReceiver != null) { + try { + context.unregisterReceiver(mReceiver); + } catch (Exception ignored) {} + mWeatherReceiverRegistered = false; + } + } + + private String getTemperatureUnit() { + return mMetric ? "\u00b0C" : "\u00b0F"; + } + + private String getWindUnit() { + return mMetric ? "km/h" : "mph"; + } + + public Drawable getWeatherConditionImage(Context context, int conditionCode) { + if (mRes == null) { + loadDefaultIconsPackage(context); + } + try { + int resId = mRes.getIdentifier(mIconPrefix + "_" + conditionCode, "drawable", mPackageName); + Drawable d = mRes.getDrawable(resId, null); + return d != null ? d : getDefaultConditionImage(context); + } catch (Exception e) { + Log.e(TAG, "getWeatherConditionImage", e); + return getDefaultConditionImage(context); + } + } + + private Drawable getDefaultConditionImage(Context context) { + try { + Resources res = context.getPackageManager().getResourcesForApplication(ICON_PACKAGE_DEFAULT); + int resId = res.getIdentifier(ICON_PREFIX_DEFAULT + "_na", "drawable", ICON_PACKAGE_DEFAULT); + Drawable d = res.getDrawable(resId, null); + return d != null ? d : new ColorDrawable(Color.RED); + } catch (Exception e) { + return new ColorDrawable(Color.RED); + } + } + + public Drawable getResOmni(Context context, String iconOmni) { + if (mRes == null) loadDefaultIconsPackage(context); + try { + int resId = mRes.getIdentifier(iconOmni, "drawable", mPackageName); + Drawable d = mRes.getDrawable(resId, null); + return d != null ? d : new ColorDrawable(Color.RED); + } catch (Exception e) { + Log.e(TAG, "getResOmni", e); + return new ColorDrawable(Color.RED); + } + } + + public static class WeatherInfo { + public String city; + public String windSpeed; + public String windDirection; + public int conditionCode; + public String temp; + public String humidity; + public String condition; + public Long timeStamp; + public List forecasts; + public String tempUnits; + public String windUnits; + public String provider; + public String pinWheel; + public String iconPack; + + public String getLastUpdateTime() { + return new SimpleDateFormat("HH:mm:ss", Locale.getDefault()).format(new Date(timeStamp)); + } + + @Override + public String toString() { + return city + " @ " + new Date(timeStamp) + " | " + condition + " | " + temp; + } + } + + public static class DayForecast { + public String low; + public String high; + public int conditionCode; + public String condition; + public String date; + + @Override + public String toString() { + return "[" + date + " - " + low + "/" + high + " - " + condition + "]"; + } + } +} diff --git a/core/java/com/android/internal/util/crdroid/OnTheGoUtils.java b/core/java/com/android/internal/util/crdroid/OnTheGoUtils.java new file mode 100644 index 0000000000000..8823dfbf768cc --- /dev/null +++ b/core/java/com/android/internal/util/crdroid/OnTheGoUtils.java @@ -0,0 +1,106 @@ +/* +* +*/ + +package com.android.internal.util.crdroid; + +import android.app.ActivityManager; +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.util.Log; + +import java.util.List; + +public class OnTheGoUtils { + + private static final String TAG = "OnTheGoUtils"; + + /** + * Checks if a specific package is installed. + * + * @param context The context to retrieve the package manager + * @param packageName The name of the package + * @return Whether the package is installed or not. + */ + public static boolean isPackageInstalled(Context context, String packageName) { + PackageManager pm = context.getPackageManager(); + try { + if (pm != null) { + List packages = pm.getInstalledApplications(0); + for (ApplicationInfo packageInfo : packages) { + if (packageInfo.packageName.equals(packageName)) { + return true; + } + } + } + } catch (Exception e) { + Log.e(TAG, "Error: " + e.getMessage()); + } + return false; + } + + /** + * Checks if a specific service is running. + * + * @param context The context to retrieve the activity manager + * @param serviceName The name of the service + * @return Whether the service is running or not + */ + public static boolean isServiceRunning(Context context, String serviceName) { + ActivityManager activityManager = (ActivityManager) context + .getSystemService(Context.ACTIVITY_SERVICE); + List services = activityManager + .getRunningServices(Integer.MAX_VALUE); + + if (services != null) { + for (ActivityManager.RunningServiceInfo info : services) { + if (info.service != null) { + if (info.service.getClassName() != null && info.service.getClassName() + .equalsIgnoreCase(serviceName)) { + return true; + } + } + } + } + + return false; + } + + /** + * Check if system has a camera. + * + * @param context + * @return + */ + public static boolean hasCamera(final Context context) { + final PackageManager pm = context.getPackageManager(); + return pm != null && pm.hasSystemFeature(PackageManager.FEATURE_CAMERA); + } + + /** + * Check if system has a front camera. + * + * @param context + * @return + */ + public static boolean hasFrontCamera(final Context context) { + final PackageManager pm = context.getPackageManager(); + return pm != null && pm.hasSystemFeature(PackageManager.FEATURE_CAMERA_FRONT); + } +} diff --git a/core/java/com/android/internal/util/crdroid/PixelPropsUtils.java b/core/java/com/android/internal/util/crdroid/PixelPropsUtils.java new file mode 100644 index 0000000000000..cb75c6d809fe0 --- /dev/null +++ b/core/java/com/android/internal/util/crdroid/PixelPropsUtils.java @@ -0,0 +1,492 @@ +/* + * Copyright (C) 2020 The Pixel Experience Project + * 2021-2025 crDroid Android Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.util.crdroid; + +import android.app.ActivityThread; +import android.app.Application; +import android.content.Context; +import android.content.res.Configuration; +import android.os.Build; +import android.os.Environment; +import android.os.SystemProperties; +import android.provider.Settings; +import android.text.TextUtils; +import android.util.ArraySet; +import android.util.Log; + +import com.android.internal.R; +import com.android.internal.util.crdroid.KeyProviderManager; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +/** + * @hide + */ +public final class PixelPropsUtils { + + private static final String TAG = PixelPropsUtils.class.getSimpleName(); + private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + private static final String DATA_FILE = "gms_certified_props.json"; + + private static final Map propsToChangeGeneric = new HashMap<>(); + private static final Map propsToChangePixel10ProXL = new HashMap<>(); + private static final Map propsToChangePixelTablet = new HashMap<>(); + private static final Map propsToChangePixelXL = new HashMap<>(); + private static final Map propsToChangeROG6 = new HashMap<>(); + private static final Map propsToChangeROG6D = new HashMap<>(); + private static final Map propsToChangeLenovoY700 = new HashMap<>(); + private static final Map propsToChangeOP8P = new HashMap<>(); + private static final Map propsToChangeOP9P = new HashMap<>(); + private static final Map propsToChangeMI11TP = new HashMap<>(); + private static final Map propsToChangeMI13P = new HashMap<>(); + private static final Map propsToChangeF5 = new HashMap<>(); + private static final Map propsToChangeBS4 = new HashMap<>(); + private static final Map propsToChangeS24U = new HashMap<>(); + + private static final ArraySet PKGS_RECENT_PIXEL = new ArraySet<>(); // Pixel device + private static final ArraySet PKGS_ROG6 = new ArraySet<>(); // ROG Phone 6 + private static final ArraySet PKGS_ROG6D = new ArraySet<>(); // ROG Phone 6D + private static final ArraySet PKGS_LENOVOY700 = new ArraySet<>(); // Lenovo Y700 + private static final ArraySet PKGS_OP8P = new ArraySet<>(); // OnePlus 8 Pro + private static final ArraySet PKGS_OP9P = new ArraySet<>(); // OnePlus 9 Pro + private static final ArraySet PKGS_MI11TP = new ArraySet<>(); // Mi 11T Pro + private static final ArraySet PKGS_MI13P = new ArraySet<>(); // Xiaomi 13 Pro + private static final ArraySet PKGS_F5 = new ArraySet<>(); // POCO F5 + private static final ArraySet PKGS_BS4 = new ArraySet<>(); // Black Shark 4 + private static final ArraySet PKGS_S24U = new ArraySet<>(); // Samsung S24 Ultra + + static { + Collections.addAll(PKGS_RECENT_PIXEL, + "com.google.android.aicore", + "com.google.android.apps.aiwallpapers", + "com.google.android.apps.bard", + "com.google.android.apps.customization.pixel", + "com.google.android.apps.emojiwallpaper", + "com.google.android.apps.nexuslauncher", + "com.google.android.apps.photos", + "com.google.android.apps.pixel.agent", + "com.google.android.apps.pixel.creativeassistant", + "com.google.android.apps.pixel.nowplaying", + "com.google.android.apps.pixel.psi", + "com.google.android.apps.pixel.subzero", + "com.google.android.apps.pixel.support", + "com.google.android.apps.privacy.wildlife", + "com.google.android.apps.wallpaper", + "com.google.android.apps.wallpaper.pixel", + "com.google.android.apps.weather", + "com.google.android.gms", + "com.google.android.googlequicksearchbox", + "com.google.android.pcs", + "com.google.android.settings.intelligence", + "com.google.android.wallpaper.effects", + "com.google.pixel.livewallpaper", + "com.netflix.mediaclient", + "com.nhs.online.nhsonline" + ); + + Collections.addAll(PKGS_ROG6, + "com.ea.gp.fifamobile", + "com.gameloft.android.ANMP.GloftA9HM", + "com.madfingergames.legends", + "com.pearlabyss.blackdesertm", + "com.pearlabyss.blackdesertm.gl" + ); + + Collections.addAll(PKGS_ROG6D, + "com.proxima.dfm" + ); + + Collections.addAll(PKGS_LENOVOY700, + "com.activision.callofduty.shooter", + "com.garena.game.codm", + "com.tencent.tmgp.kr.codm", + "com.vng.codmvn" + ); + + Collections.addAll(PKGS_OP8P, + "com.netease.lztgglobal", + "com.riotgames.league.wildrift", + "com.riotgames.league.wildrifttw", + "com.riotgames.league.wildriftvn", + "com.riotgames.league.teamfighttactics", + "com.riotgames.league.teamfighttacticstw", + "com.riotgames.league.teamfighttacticsvn" + ); + + Collections.addAll(PKGS_OP9P, + "com.epicgames.fortnite", + "com.epicgames.portal", + "com.tencent.lolm" + ); + + Collections.addAll(PKGS_MI11TP, + "com.ea.gp.apexlegendsmobilefps", + "com.levelinfinite.hotta.gp", + "com.supercell.clashofclans", + "com.vng.mlbbvn" + ); + + Collections.addAll(PKGS_MI13P, + "com.levelinfinite.sgameGlobal", + "com.tencent.tmgp.sgame" + ); + + Collections.addAll(PKGS_F5, + "com.dts.freefiremax", + "com.dts.freefireth", + "com.mobile.legends" + ); + + Collections.addAll(PKGS_BS4, + "com.proximabeta.mf.uamo" + ); + + Collections.addAll(PKGS_S24U, + "com.blizzard.diablo.immortal", + "com.pubg.imobile", + "com.pubg.krmobile", + "com.rekoo.pubgm", + "com.tencent.ig", + "com.tencent.tmgp.pubgmhd", + "com.vng.pubgmobile" + ); + + propsToChangeGeneric.put("TYPE", "user"); + propsToChangeGeneric.put("TAGS", "release-keys"); + propsToChangePixel10ProXL.put("BRAND", "google"); + propsToChangePixel10ProXL.put("MANUFACTURER", "Google"); + propsToChangePixel10ProXL.put("DEVICE", "mustang"); + propsToChangePixel10ProXL.put("PRODUCT", "mustang"); + propsToChangePixel10ProXL.put("HARDWARE", "mustang"); + propsToChangePixel10ProXL.put("MODEL", "Pixel 10 Pro XL"); + propsToChangePixel10ProXL.put("ID", "BP4A.260105.004.E1"); + propsToChangePixel10ProXL.put("FINGERPRINT", "google/mustang/mustang:16/BP4A.260105.004.E1/14587043:user/release-keys"); + propsToChangePixelTablet.put("BRAND", "google"); + propsToChangePixelTablet.put("MANUFACTURER", "Google"); + propsToChangePixelTablet.put("DEVICE", "tangorpro"); + propsToChangePixelTablet.put("PRODUCT", "tangorpro"); + propsToChangePixelTablet.put("HARDWARE", "tangorpro"); + propsToChangePixelTablet.put("MODEL", "Pixel Tablet"); + propsToChangePixelTablet.put("ID", "BP4A.260105.004.E1"); + propsToChangePixelTablet.put("FINGERPRINT", "google/tangorpro/tangorpro:16/BP4A.260105.004.E1/14587043:user/release-keys"); + propsToChangePixelXL.put("BRAND", "google"); + propsToChangePixelXL.put("MANUFACTURER", "Google"); + propsToChangePixelXL.put("DEVICE", "marlin"); + propsToChangePixelXL.put("PRODUCT", "marlin"); + propsToChangePixelXL.put("HARDWARE", "marlin"); + propsToChangePixelXL.put("MODEL", "Pixel XL"); + propsToChangePixelXL.put("ID", "QP1A.191005.007.A3"); + propsToChangePixelXL.put("FINGERPRINT", "google/marlin/marlin:10/QP1A.191005.007.A3/5972272:user/release-keys"); + propsToChangeROG6.put("BRAND", "asus"); + propsToChangeROG6.put("MANUFACTURER", "asus"); + propsToChangeROG6.put("DEVICE", "AI2201"); + propsToChangeROG6.put("MODEL", "ASUS_AI2201"); + propsToChangeROG6D.put("BRAND", "asus"); + propsToChangeROG6D.put("MANUFACTURER", "asus"); + propsToChangeROG6D.put("DEVICE", "AI2203_C"); + propsToChangeROG6D.put("MODEL", "ASUS_AI2203_C"); + propsToChangeLenovoY700.put("MODEL", "Lenovo TB-9707F"); + propsToChangeLenovoY700.put("MANUFACTURER", "lenovo"); + propsToChangeOP8P.put("MODEL", "IN2020"); + propsToChangeOP8P.put("MANUFACTURER", "OnePlus"); + propsToChangeOP9P.put("MODEL", "LE2123"); + propsToChangeOP9P.put("MANUFACTURER", "OnePlus"); + propsToChangeMI11TP.put("MODEL", "2107113SI"); + propsToChangeMI11TP.put("MANUFACTURER", "Xiaomi"); + propsToChangeMI13P.put("BRAND", "Xiaomi"); + propsToChangeMI13P.put("MANUFACTURER", "Xiaomi"); + propsToChangeMI13P.put("MODEL", "2210132C"); + propsToChangeF5.put("MODEL", "23049PCD8G"); + propsToChangeF5.put("MANUFACTURER", "Xiaomi"); + propsToChangeBS4.put("MODEL", "2SM-X706B"); + propsToChangeBS4.put("MANUFACTURER", "blackshark"); + propsToChangeS24U.put("BRAND", "samsung"); + propsToChangeS24U.put("MANUFACTURER", "samsung"); + propsToChangeS24U.put("MODEL", "SM-S928B"); + } + + private static volatile List sCertifiedProps; + private static volatile long sCertPropsMtime = -1; + + public static void setProps(Context context) { + final String packageName = context.getPackageName(); + if (packageName == null || packageName.isEmpty()) { + if (DEBUG) Log.d(TAG, "Null received in setProps."); + return; + } + + if (android.os.Process.isIsolated()) { + if (DEBUG) Log.d(TAG, "Skipping setProps in isolated process"); + return; + } + + propsToChangeGeneric.forEach((k, v) -> setPropValue(k, v)); + + if (PKGS_RECENT_PIXEL.contains(packageName)) { + Map propsToChange = null; + + if (packageName.equals("com.google.android.apps.photos")) { + if (Settings.Secure.getInt(context.getContentResolver(), + Settings.Secure.PI_PHOTOS_SPOOF, 1) == 1) { + if (DEBUG) Log.d(TAG, "Gphotos spoofing disabled by setting"); + propsToChange = propsToChangePixelXL; + } + } else if (packageName.equals("com.netflix.mediaclient") && + Settings.Secure.getInt(context.getContentResolver(), + Settings.Secure.PI_NETFLIX_SPOOF, 0) != 1) { + if (DEBUG) Log.d(TAG, "Netflix spoofing disabled by setting"); + return; + } else if (packageName.equals("com.google.android.gms")) { + final String processName = Application.getProcessName().toLowerCase(); + if (processName.contains("unstable")) { + spoofBuildGms(context); + return; + } + return; + } else if (packageName.equals("com.google.android.settings.intelligence")) { + setPropValue("FINGERPRINT", Build.VERSION.INCREMENTAL); + return; + } else { + if (isDeviceTablet(context.getApplicationContext())) { + propsToChange = propsToChangePixelTablet; + } else { + propsToChange = propsToChangePixel10ProXL; + } + } + + if (propsToChange != null) { + if (DEBUG) Log.d(TAG, "Defining props for: " + packageName); + applyProps(propsToChange); + } + } else if (isGamePackage(packageName)) { + if (Settings.Secure.getInt(context.getContentResolver(), + Settings.Secure.PI_GAMES_SPOOF, 0) != 1) + return; + + Map propsToChange = null; + + if (PKGS_ROG6.contains(packageName)) { + propsToChange = propsToChangeROG6; + } else if (PKGS_ROG6D.contains(packageName)) { + propsToChange = propsToChangeROG6D; + } else if (PKGS_LENOVOY700.contains(packageName)) { + propsToChange = propsToChangeLenovoY700; + } else if (PKGS_OP8P.contains(packageName)) { + propsToChange = propsToChangeOP8P; + } else if (PKGS_OP9P.contains(packageName)) { + propsToChange = propsToChangeOP9P; + } else if (PKGS_MI11TP.contains(packageName)) { + propsToChange = propsToChangeMI11TP; + } else if (PKGS_MI13P.contains(packageName)) { + propsToChange = propsToChangeMI13P; + } else if (PKGS_F5.contains(packageName)) { + propsToChange = propsToChangeF5; + } else if (PKGS_BS4.contains(packageName)) { + propsToChange = propsToChangeBS4; + } else if (PKGS_S24U.contains(packageName)) { + propsToChange = propsToChangeS24U; + } + + if (propsToChange != null) { + if (DEBUG) Log.d(TAG, "Defining props for: " + packageName); + applyProps(propsToChange); + } + } + } + + private static boolean isGamePackage(String pkg) { + return PKGS_ROG6.contains(pkg) + || PKGS_ROG6D.contains(pkg) + || PKGS_LENOVOY700.contains(pkg) + || PKGS_OP8P.contains(pkg) + || PKGS_OP9P.contains(pkg) + || PKGS_MI11TP.contains(pkg) + || PKGS_MI13P.contains(pkg) + || PKGS_F5.contains(pkg) + || PKGS_BS4.contains(pkg) + || PKGS_S24U.contains(pkg); + } + + private static void applyProps(Map props) { + for (Map.Entry e : props.entrySet()) + setPropValue(e.getKey(), e.getValue()); + } + + private static boolean isDeviceTablet(Context context) { + if (context == null) { + if (DEBUG) Log.d(TAG, "Null received in isDeviceTablet."); + return false; + } + Configuration config = context.getResources().getConfiguration(); + boolean isTablet = (config.smallestScreenWidthDp >= 600); + return isTablet; + } + + private static void setPropValue(String key, Object value) { + setPropValue(key, String.valueOf(value)); + } + + private static void setPropValue(String key, String value) { + try { + if (DEBUG) Log.d(TAG, "Defining prop " + key + " to " + value); + Class clazz = Build.class; + if (key.startsWith("VERSION.")) { + clazz = Build.VERSION.class; + key = key.substring(8); + } + Field field = clazz.getDeclaredField(key); + field.setAccessible(true); + // Determine the field type and parse the value accordingly. + if (field.getType().equals(Integer.TYPE)) { + field.set(null, Integer.parseInt(value)); + } else if (field.getType().equals(Long.TYPE)) { + field.set(null, Long.parseLong(value)); + } else { + field.set(null, value); + } + field.setAccessible(false); + } catch (Exception e) { + Log.e(TAG, "Failed to set prop " + key, e); + } + } + + private static void spoofBuildGms(Context context) { + if (Settings.Secure.getInt(context.getContentResolver(), + Settings.Secure.PI_ENABLE_SPOOF, 1) != 1) { + if (DEBUG) Log.d(TAG, "GMS spoofing disabled by setting"); + return; + } + + File dataFile = new File(Environment.getDataSystemDirectory(), DATA_FILE); + long mtime = dataFile.exists() ? dataFile.lastModified() : -1; + + if (mtime == sCertPropsMtime && sCertifiedProps != null && !sCertifiedProps.isEmpty()) { + if (DEBUG) Log.d(TAG, "New certification props not found, applying existing ones"); + applyCertifiedProps(); + return; + } + + String savedProps = readFromFile(dataFile); + List fresh = new ArrayList<>(); + if (TextUtils.isEmpty(savedProps)) { + if (DEBUG) Log.d(TAG, "Certification props not available! Not applied."); + return; + } + if (DEBUG) Log.d(TAG, "Parsing props fetched by attestation service"); + try { + JSONObject parsedProps = new JSONObject(savedProps); + Iterator keys = parsedProps.keys(); + while (keys.hasNext()) { + String key = keys.next(); + String value = parsedProps.getString(key); + fresh.add(key + ":" + value); + } + } catch (JSONException e) { + Log.e(TAG, "Error parsing JSON data", e); + return; + } + sCertifiedProps = new ArrayList<>(fresh); + sCertPropsMtime = mtime; + if (sCertifiedProps != null && !sCertifiedProps.isEmpty()) { + if (DEBUG) Log.d(TAG, "New certification props found, applying new ones"); + applyCertifiedProps(); + } + } + + private static void applyCertifiedProps() { + for (String entry : sCertifiedProps) { + String[] kv = entry.split(":", 2); + if (kv.length == 2) setPropValue(kv[0], kv[1]); + } + } + + private static String readFromFile(File file) { + StringBuilder content = new StringBuilder(); + + if (file.exists()) { + try (BufferedReader reader = new BufferedReader(new FileReader(file))) { + String line; + + while ((line = reader.readLine()) != null) { + content.append(line); + } + } catch (IOException e) { + Log.e(TAG, "Error reading from file", e); + } + } + return content.toString(); + } + + private static boolean isCallerSafetyNet() { + for (StackTraceElement e : Thread.currentThread().getStackTrace()) { + final String cn = e.getClassName(); + if (cn != null && (cn.contains("DroidGuard") || cn.contains("droidguard"))) return true; + } + return false; + } + + public static void onEngineGetCertificateChain() { + if (android.os.Process.isIsolated()) { + if (DEBUG) Log.d(TAG, "Skipping onEngineGetCertificateChain in isolated process"); + return; + } + + Context context = ActivityThread.currentApplication() != null + ? ActivityThread.currentApplication().getApplicationContext() + : null; + if (context == null) { + if (DEBUG) Log.d(TAG, "Null received in onEngineGetCertificateChain."); + return; + } + + boolean isKeyBoxAvailable = KeyProviderManager.isKeyboxAvailable(); + + if (!isKeyBoxAvailable && Settings.Secure.getInt(context.getContentResolver(), + Settings.Secure.PI_ENABLE_SPOOF, 1) != 1) { + if (DEBUG) Log.d(TAG, "onEngineGetCertificateChain disabled by setting"); + return; + } + + // If a keybox is found, don't block key attestation + if (isKeyBoxAvailable && Settings.Secure.getInt(context.getContentResolver(), + Settings.Secure.PI_GMS_CERT_CHAIN, 0) == 1) { + Log.i(TAG, "Key attestation blocking is disabled because a keybox is defined to spoof"); + return; + } + + // Check stack for SafetyNet + if (isCallerSafetyNet()) { + Log.i(TAG, "Blocked key attestation"); + throw new UnsupportedOperationException(); + } + } +} diff --git a/core/java/com/android/internal/util/crdroid/ThemeUtils.java b/core/java/com/android/internal/util/crdroid/ThemeUtils.java new file mode 100644 index 0000000000000..f898586bfbec3 --- /dev/null +++ b/core/java/com/android/internal/util/crdroid/ThemeUtils.java @@ -0,0 +1,234 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.util.crdroid; + +import static android.os.UserHandle.USER_SYSTEM; + +import android.util.PathParser; + +import android.content.ContentResolver; +import android.content.Context; +import android.content.om.IOverlayManager; +import android.content.om.OverlayInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.ProviderInfo; +import android.content.res.Resources; +import android.content.res.Resources.NotFoundException; +import android.content.res.Configuration; +import android.database.Cursor; +import android.graphics.Typeface; +import android.graphics.Path; +import android.graphics.drawable.AdaptiveIconDrawable; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.ShapeDrawable; +import android.graphics.drawable.shapes.PathShape; +import android.net.Uri; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.UserHandle; +import android.provider.Settings; +import android.text.TextUtils; +import android.util.Log; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +public class ThemeUtils { + + public static final String TAG = "ThemeUtils"; + + public static final String FONT_KEY = "android.theme.customization.font"; + public static final String ICON_SHAPE_KEY= "android.theme.customization.adaptive_icon_shape"; + + public static final Comparator OVERLAY_INFO_COMPARATOR = + Comparator.comparingInt(a -> a.priority); + + private Context mContext; + private IOverlayManager mOverlayManager; + private PackageManager pm; + private Resources overlayRes; + + public ThemeUtils(Context context) { + mContext = context; + mOverlayManager = IOverlayManager.Stub + .asInterface(ServiceManager.getService(Context.OVERLAY_SERVICE)); + pm = context.getPackageManager(); + } + + public void setOverlayEnabled(String category, String packageName, String target) { + final String currentPackageName = getOverlayInfos(category, target).stream() + .filter(info -> info.isEnabled()) + .map(info -> info.packageName) + .findFirst() + .orElse(null); + + try { + if (target.equals(packageName)) { + if (currentPackageName != null) { + mOverlayManager.setEnabled(currentPackageName, false, USER_SYSTEM); + } + } else { + mOverlayManager.setEnabledExclusiveInCategory(packageName, USER_SYSTEM); + } + + writeSettings(category, packageName, target.equals(packageName)); + + } catch (RemoteException e) { + Log.e(TAG, "RemoteException while setting overlay: " + e.getMessage(), e); + } + } + + public void writeSettings(String category, String packageName, boolean disable) { + final String overlayPackageJson = Settings.Secure.getStringForUser( + mContext.getContentResolver(), + Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES, UserHandle.USER_CURRENT); + JSONObject object; + try { + if (overlayPackageJson == null) { + object = new JSONObject(); + } else { + object = new JSONObject(overlayPackageJson); + } + if (disable) { + if (object.has(category)) object.remove(category); + } else { + object.put(category, packageName); + } + Settings.Secure.putStringForUser(mContext.getContentResolver(), + Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES, + object.toString(), UserHandle.USER_CURRENT); + } catch (JSONException e) { + Log.e(TAG, "Failed to parse THEME_CUSTOMIZATION_OVERLAY_PACKAGES.", e); + } + } + + public List getOverlayPackagesForCategory(String category) { + return getOverlayPackagesForCategory(category, "android"); + } + + public List getOverlayPackagesForCategory(String category, String target) { + List overlays = new ArrayList<>(); + List mPkgs = new ArrayList<>(); + overlays.add(target); + for (OverlayInfo info : getOverlayInfos(category, target)) { + if (category.equals(info.getCategory())) { + mPkgs.add(info.getPackageName()); + } + } + Collections.sort(mPkgs); + overlays.addAll(mPkgs); + return overlays; + } + + public List getOverlayInfos(String category) { + return getOverlayInfos(category, "android"); + } + + public List getOverlayInfos(String category, String target) { + final List filteredInfos = new ArrayList<>(); + try { + List overlayInfos = mOverlayManager + .getOverlayInfosForTarget(target, USER_SYSTEM); + for (OverlayInfo overlayInfo : overlayInfos) { + if (category.equals(overlayInfo.category)) { + filteredInfos.add(overlayInfo); + } + } + } catch (RemoteException re) { + Log.e(TAG, "RemoteException while getting overlay info: " + re.getMessage(), re); + } + filteredInfos.sort(OVERLAY_INFO_COMPARATOR); + return filteredInfos; + } + + public List getFonts() { + final List fontlist = new ArrayList<>(); + for (String overlayPackage : getOverlayPackagesForCategory(FONT_KEY)) { + Resources overlayRes = null; + try { + overlayRes = overlayPackage.equals("android") ? Resources.getSystem() + : pm.getResourcesForApplication(overlayPackage); + if (overlayRes != null) { + int fontId = overlayRes.getIdentifier("config_bodyFontFamily", "string", overlayPackage); + if (fontId != 0) { + String fontName = overlayRes.getString(fontId); + fontlist.add(Typeface.create(fontName, Typeface.NORMAL)); + } + } + } catch (NameNotFoundException | NotFoundException e) { + Log.e(TAG, "Error fetching fonts for package: " + overlayPackage, e); + } + } + return fontlist; + } + + public List getShapeDrawables() { + final List shapelist = new ArrayList<>(); + for (String overlayPackage : getOverlayPackagesForCategory(ICON_SHAPE_KEY)) { + shapelist.add(createShapeDrawable(overlayPackage)); + } + return shapelist; + } + + public ShapeDrawable createShapeDrawable(String overlayPackage) { + try { + if (overlayPackage.equals("android")) { + overlayRes = Resources.getSystem(); + } else { + if (overlayPackage.equals("default")) overlayPackage = "android"; + overlayRes = pm.getResourcesForApplication(overlayPackage); + } + } catch (NameNotFoundException | NotFoundException e) { + // Do nothing + } + if (overlayRes == null) { + Log.e(TAG, "Resources not found for package: " + overlayPackage); + return null; + } + final String shape = overlayRes.getString( + overlayRes.getIdentifier("config_icon_mask", + "string", overlayPackage)); + Path path = TextUtils.isEmpty(shape) ? null : PathParser.createPathFromPathData(shape); + PathShape pathShape = new PathShape(path, 100f, 100f); + ShapeDrawable shapeDrawable = new ShapeDrawable(pathShape); + int mThumbSize = (int) (mContext.getResources().getDisplayMetrics().density * 72); + shapeDrawable.setIntrinsicHeight(mThumbSize); + shapeDrawable.setIntrinsicWidth(mThumbSize); + return shapeDrawable; + } + + public boolean isOverlayEnabled(String overlayPackage) { + try { + OverlayInfo info = mOverlayManager.getOverlayInfo(overlayPackage, USER_SYSTEM); + return info == null ? false : info.isEnabled(); + } catch (RemoteException e) { + Log.e(TAG, "RemoteException while checking if overlay is enabled: " + e.getMessage(), e); + } + return false; + } + + public boolean isDefaultOverlay(String category) { + return getOverlayPackagesForCategory(category).stream() + .noneMatch(pkg -> isOverlayEnabled(pkg)); + } +} diff --git a/core/java/com/android/internal/util/crdroid/Utils.java b/core/java/com/android/internal/util/crdroid/Utils.java new file mode 100644 index 0000000000000..9ae9143e2745f --- /dev/null +++ b/core/java/com/android/internal/util/crdroid/Utils.java @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2017-2025 crDroid Android Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.util.crdroid; + +import android.app.ActivityThread; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.os.PowerManager; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.SystemClock; +import android.os.SystemProperties; +import android.os.UserHandle; +import android.provider.Settings; + +import com.android.internal.statusbar.IStatusBarService; + +import java.util.ArrayList; +import java.util.List; + +public class Utils { + + public static boolean isPackageInstalled(Context context, String packageName, boolean ignoreState) { + if (packageName != null) { + try { + PackageInfo pi = context.getPackageManager().getPackageInfo(packageName, 0); + if (!pi.applicationInfo.enabled && !ignoreState) { + return false; + } + } catch (PackageManager.NameNotFoundException e) { + return false; + } + } + return true; + } + + public static boolean isPackageInstalled(Context context, String packageName) { + return isPackageInstalled(context, packageName, true); + } + + public static boolean isPackageEnabled(Context context, String packageName) { + try { + PackageInfo pi = context.getPackageManager().getPackageInfo(packageName, 0); + return pi.applicationInfo.enabled; + } catch (PackageManager.NameNotFoundException notFound) { + return false; + } + } + + public static List launchablePackages(Context context) { + List list = new ArrayList<>(); + + Intent filter = new Intent(Intent.ACTION_MAIN, null); + filter.addCategory(Intent.CATEGORY_LAUNCHER); + + List apps = context.getPackageManager().queryIntentActivities(filter, + PackageManager.GET_META_DATA); + + int numPackages = apps.size(); + for (int i = 0; i < numPackages; i++) { + ResolveInfo app = apps.get(i); + list.add(app.activityInfo.packageName); + } + + return list; + } + + public static void switchScreenOff(Context ctx) { + PowerManager pm = (PowerManager) ctx.getSystemService(Context.POWER_SERVICE); + if (pm!= null) { + pm.goToSleep(SystemClock.uptimeMillis()); + } + } + + public static boolean deviceHasFlashlight(Context ctx) { + return ctx.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_FLASH); + } + + public static boolean hasNavbarByDefault(Context context) { + boolean needsNav = context.getResources().getBoolean( + com.android.internal.R.bool.config_showNavigationBar); + String navBarOverride = SystemProperties.get("qemu.hw.mainkeys"); + if ("1".equals(navBarOverride)) { + needsNav = false; + } else if ("0".equals(navBarOverride)) { + needsNav = true; + } + return needsNav; + } + + public static void restartSystemUI() { + final IStatusBarService mBarService = IStatusBarService.Stub.asInterface( + ServiceManager.getService(Context.STATUS_BAR_SERVICE)); + try { + mBarService.restartSystemUI(); + } catch (RemoteException e) { + } + } + + public static boolean ambientAod() { + try { + Context ctx = ActivityThread.currentApplication() != null + ? ActivityThread.currentApplication().getApplicationContext() + : null; + if (ctx == null) return false; + return Settings.Secure.getIntForUser(ctx.getContentResolver(), + Settings.Secure.DOZE_ALWAYS_ON_WALLPAPER_ENABLED, + ctx.getResources().getBoolean( + com.android.internal.R.bool.config_dozeSupportsAodWallpaper) ? 1 : 0, + UserHandle.USER_CURRENT) == 1; + } catch (Throwable t) { + return false; + } + } +} diff --git a/core/java/com/android/internal/util/crdroid/cutout/CutoutFullscreenController.java b/core/java/com/android/internal/util/crdroid/cutout/CutoutFullscreenController.java new file mode 100644 index 0000000000000..6dcd7034f311a --- /dev/null +++ b/core/java/com/android/internal/util/crdroid/cutout/CutoutFullscreenController.java @@ -0,0 +1,118 @@ +/** + * Copyright (C) 2018 The LineageOS project + * Copyright (C) 2019 The PixelExperience project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.util.crdroid.cutout; + +import android.content.ContentResolver; +import android.content.Context; +import android.content.res.Resources; +import android.database.ContentObserver; +import android.os.Handler; +import android.os.Looper; +import android.os.UserHandle; +import android.text.TextUtils; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +import android.provider.Settings; + +public class CutoutFullscreenController { + private Set mApps = new HashSet<>(); + private Context mContext; + + private final boolean isAvailable; + + public CutoutFullscreenController(Context context) { + mContext = context; + final Resources resources = mContext.getResources(); + + final String displayCutout = resources.getString(com.android.internal.R.string.config_mainBuiltInDisplayCutout); + isAvailable = !TextUtils.isEmpty(displayCutout); + + if (!isAvailable) { + return; + } + + SettingsObserver observer = new SettingsObserver( + new Handler(Looper.getMainLooper())); + observer.observe(); + } + + public boolean isSupported() { + return isAvailable; + } + + public boolean shouldForceCutoutFullscreen(String packageName) { + return isSupported() && mApps.contains(packageName); + } + + public Set getApps() { + return mApps; + } + + public void addApp(String packageName) { + mApps.add(packageName); + Settings.System.putString(mContext.getContentResolver(), + Settings.System.FORCE_FULLSCREEN_CUTOUT_APPS, String.join(",", mApps)); + } + + public void removeApp(String packageName) { + mApps.remove(packageName); + Settings.System.putString(mContext.getContentResolver(), + Settings.System.FORCE_FULLSCREEN_CUTOUT_APPS, String.join(",", mApps)); + } + + public void setApps(Set apps) { + mApps = apps; + } + + class SettingsObserver extends ContentObserver { + SettingsObserver(Handler handler) { + super(handler); + } + + void observe() { + ContentResolver resolver = mContext.getContentResolver(); + + resolver.registerContentObserver(Settings.System.getUriFor( + Settings.System.FORCE_FULLSCREEN_CUTOUT_APPS), false, this, + UserHandle.USER_ALL); + + update(); + } + + @Override + public void onChange(boolean selfChange) { + update(); + } + + public void update() { + ContentResolver resolver = mContext.getContentResolver(); + + String apps = Settings.System.getStringForUser(resolver, + Settings.System.FORCE_FULLSCREEN_CUTOUT_APPS, + UserHandle.USER_CURRENT); + if (apps != null) { + setApps(new HashSet<>(Arrays.asList(apps.split(",")))); + } else { + setApps(new HashSet<>()); + } + } + } +} diff --git a/core/java/com/android/internal/view/RotationPolicy.java b/core/java/com/android/internal/view/RotationPolicy.java index 93e36bcedb7b8..98d50c782b47a 100644 --- a/core/java/com/android/internal/view/RotationPolicy.java +++ b/core/java/com/android/internal/view/RotationPolicy.java @@ -28,6 +28,8 @@ import android.os.RemoteException; import android.os.UserHandle; import android.provider.Settings; +import android.sysprop.SurfaceFlingerProperties; +import android.sysprop.SurfaceFlingerProperties.primary_display_orientation_values; import android.util.DisplayMetrics; import android.util.Log; import android.view.IWindowManager; @@ -36,6 +38,8 @@ import com.android.internal.R; +import java.util.Optional; + /** * Provides helper functions for configuring the display rotation policy. */ @@ -43,7 +47,7 @@ public final class RotationPolicy { private static final String TAG = "RotationPolicy"; private static final int CURRENT_ROTATION = -1; - public static final int NATURAL_ROTATION = Surface.ROTATION_0; + private static int sNaturalRotation = -1; private RotationPolicy() { } @@ -74,7 +78,7 @@ public static boolean isRotationSupported(Context context) { * otherwise Configuration.ORIENTATION_UNDEFINED if any orientation is lockable. */ public static int getRotationLockOrientation(Context context) { - if (areAllRotationsAllowed(context)) { + if (isCurrentRotationAllowed(context)) { return Configuration.ORIENTATION_UNDEFINED; } final DisplayMetrics metrics = context.getResources().getDisplayMetrics(); @@ -122,9 +126,9 @@ public static boolean isRotationLocked(Context context) { * Enables or disables rotation lock from the system UI toggle. */ public static void setRotationLock(Context context, final boolean enabled, String caller) { - final int rotation = areAllRotationsAllowed(context) + final int rotation = isCurrentRotationAllowed(context) || useCurrentRotationOnRotationLockChange(context) ? CURRENT_ROTATION - : NATURAL_ROTATION; + : getNaturalRotation(); setRotationLockAtAngle(context, enabled, rotation, caller); } @@ -144,13 +148,13 @@ public static void setRotationLockAtAngle(Context context, final boolean enabled * Sets screen rotation to {@link rotation} if the value of {@link ACCELEROMETER_ROTATION} is * false. */ - public static void setRotationAtAngleIfLocked(final int rotation, String caller) { + public static void setRotationAtAngleIfAllowed(final int rotation, String caller) { AsyncTask.execute(new Runnable() { @Override public void run() { try { IWindowManager wm = WindowManagerGlobal.getWindowManagerService(); - wm.setRotationAtAngleIfLocked(rotation, caller); + wm.setRotationAtAngleIfAllowed(rotation, caller); } catch (RemoteException exc) { Log.w(TAG, "Unable to set rotation to:" + rotation); } @@ -169,15 +173,43 @@ public static void setRotationLockForAccessibility(Context context, final boolea Settings.System.HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY, enabled ? 1 : 0, UserHandle.USER_CURRENT); - setRotationLock(enabled, NATURAL_ROTATION, caller); + setRotationLock(enabled, getNaturalRotation(), caller); } - /** - * If true, the screen can be rotated via the accelerometer in all 4 rotations as the default - * behavior. - */ - public static boolean areAllRotationsAllowed(Context context) { - return context.getResources().getBoolean(R.bool.config_allowAllRotations); + public static boolean isRotationAllowed(int rotation, + int userRotationAngles, boolean allowAllRotations) { + if (userRotationAngles < 0) { + // Not set by user so use these defaults + userRotationAngles = allowAllRotations ? + (1 | 2 | 4 | 8) : // All angles + (1 | 2 | 8); // All except 180 + } + switch (rotation) { + case Surface.ROTATION_0: + return (userRotationAngles & 1) != 0; + case Surface.ROTATION_90: + return (userRotationAngles & 2) != 0; + case Surface.ROTATION_180: + return (userRotationAngles & 4) != 0; + case Surface.ROTATION_270: + return (userRotationAngles & 8) != 0; + } + return false; + } + + public static boolean isCurrentRotationAllowed(Context context) { + int userRotationAngles = Settings.System.getIntForUser(context.getContentResolver(), + Settings.System.ACCELEROMETER_ROTATION_ANGLES, -1, UserHandle.USER_CURRENT); + boolean allowAllRotations = context.getResources().getBoolean( + com.android.internal.R.bool.config_allowAllRotations); + final IWindowManager wm = WindowManagerGlobal.getWindowManagerService(); + try { + return isRotationAllowed(wm.getDefaultDisplayRotation(), userRotationAngles, + allowAllRotations); + } catch (RemoteException exc) { + Log.w(TAG, "Unable to getWindowManagerService.getDefaultDisplayRotation()"); + } + return false; } /** @@ -238,6 +270,35 @@ public static void unregisterRotationPolicyListener(Context context, context.getContentResolver().unregisterContentObserver(listener.mObserver); } + public static int getNaturalRotation() { + if (sNaturalRotation == -1) { + sNaturalRotation = getNaturalRotationConfig(); + } + return sNaturalRotation; + } + + private static int getNaturalRotationConfig() { + primary_display_orientation_values orientation = + primary_display_orientation_values.ORIENTATION_0; + Optional primaryDisplayOrientation = + SurfaceFlingerProperties.primary_display_orientation(); + if (primaryDisplayOrientation.isPresent()) { + orientation = primaryDisplayOrientation.get(); + } + + if (orientation == primary_display_orientation_values.ORIENTATION_90) { + return Surface.ROTATION_90; + } + if (orientation == primary_display_orientation_values.ORIENTATION_180) { + return Surface.ROTATION_180; + } + if (orientation == primary_display_orientation_values.ORIENTATION_270) { + return Surface.ROTATION_270; + } + + return Surface.ROTATION_0; + } + /** * Listener that is invoked whenever a change occurs that might affect the rotation policy. */ diff --git a/core/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl index a406a3f0cbfc0..d3244c1fb4009 100644 --- a/core/java/com/android/internal/widget/ILockSettings.aidl +++ b/core/java/com/android/internal/widget/ILockSettings.aidl @@ -111,4 +111,5 @@ interface ILockSettings { boolean isWeakEscrowTokenValid(long handle, in byte[] token, int userId); void unlockUserKeyIfUnsecured(int userId); boolean writeRepairModeCredential(int userId); + byte getLockPatternSize(int userId); } diff --git a/core/java/com/android/internal/widget/LocalImageResolver.java b/core/java/com/android/internal/widget/LocalImageResolver.java index c5d74fc5abe42..6351c0e631ac7 100644 --- a/core/java/com/android/internal/widget/LocalImageResolver.java +++ b/core/java/com/android/internal/widget/LocalImageResolver.java @@ -46,6 +46,12 @@ public class LocalImageResolver { @VisibleForTesting static final int DEFAULT_MAX_SAFE_ICON_SIZE_PX = 480; + /** + * If an image is larger than this, we won't even attempt to decode it, as we risk taking up all + * of the device memory. + */ + private static final int DEFAULT_DECODE_HARD_LIMIT_PX = 4096; + /** * Resolve an image from the given Uri using {@link ImageDecoder} if it contains a * bitmap reference. @@ -252,6 +258,16 @@ private static int getPowerOfTwoForSampleRatio(double ratio) { private static void onHeaderDecoded(ImageDecoder decoder, ImageDecoder.ImageInfo info, int maxWidth, int maxHeight) { final Size size = info.getSize(); + + if (size.getWidth() > DEFAULT_DECODE_HARD_LIMIT_PX + || size.getHeight() > DEFAULT_DECODE_HARD_LIMIT_PX) { + // The image is larger than what we can reasonably expect to decode without filling up + // the device memory, so let's bail. + throw new RuntimeException( + "Image dimensions (" + size.getWidth() + "x" + size.getHeight() + + ") exceed the maximum allowed size."); + } + final int originalSize = Math.max(size.getHeight(), size.getWidth()); final int maxSize = Math.max(maxWidth, maxHeight); final double ratio = (originalSize > maxSize) diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java index dddd958c33218..d3680623f2977 100644 --- a/core/java/com/android/internal/widget/LockPatternUtils.java +++ b/core/java/com/android/internal/widget/LockPatternUtils.java @@ -104,6 +104,11 @@ public class LockPatternUtils { */ public static final int MIN_LOCK_PASSWORD_SIZE = 4; + /* + * The default size of the pattern lockscreen. Ex: 3x3 + */ + public static final byte PATTERN_SIZE_DEFAULT = 3; + /** * The minimum number of dots the user must include in a wrong pattern attempt for it to be * counted. @@ -123,7 +128,7 @@ public class LockPatternUtils { public static final int PIN_LENGTH_UNAVAILABLE = -1; // This is the minimum pin length at which auto confirmation is supported - public static final int MIN_AUTO_PIN_REQUIREMENT_LENGTH = 6; + public static final int MIN_AUTO_PIN_REQUIREMENT_LENGTH = 4; /** * Header used for the encryption and decryption of the device credential for @@ -942,16 +947,18 @@ private boolean isCredentialShareableWithParent(int userHandle) { * @param bytes The pattern serialized with {@link #patternToByteArray} * @return The pattern. */ - public static List byteArrayToPattern(byte[] bytes) { + public static List byteArrayToPattern(byte[] bytes, byte gridSize) { if (bytes == null) { return null; } List result = Lists.newArrayList(); + LockPatternView.Cell.updateSize(gridSize); + for (int i = 0; i < bytes.length; i++) { byte b = (byte) (bytes[i] - '1'); - result.add(LockPatternView.Cell.of(b / 3, b % 3)); + result.add(LockPatternView.Cell.of(b / gridSize, b % gridSize, gridSize)); } return result; } @@ -961,7 +968,7 @@ public static List byteArrayToPattern(byte[] bytes) { * @param pattern The pattern. * @return The pattern in byte array form. */ - public static byte[] patternToByteArray(List pattern) { + public static byte[] patternToByteArray(List pattern, byte gridSize) { if (pattern == null) { return new byte[0]; } @@ -970,7 +977,7 @@ public static byte[] patternToByteArray(List pattern) { byte[] res = ArrayUtils.newNonMovableByteArray(patternSize); for (int i = 0; i < patternSize; i++) { LockPatternView.Cell cell = pattern.get(i); - res[i] = (byte) (cell.getRow() * 3 + cell.getColumn() + '1'); + res[i] = (byte) (cell.getRow() * gridSize + cell.getColumn() + '1'); } return res; } @@ -1040,6 +1047,40 @@ public final static void invalidateCredentialTypeCache() { return mCredentialTypeCache.query(userHandle); } + /** + * @return the pattern lockscreen size + */ + public byte getLockPatternSize(int userId) { + long size = getLong(Settings.Secure.LOCK_PATTERN_SIZE, -1, userId); + if (size > 0 && size < 128) { + return (byte) size; + } + return LockPatternUtils.PATTERN_SIZE_DEFAULT; + } + + /** + * Set the pattern lockscreen size + */ + public void setLockPatternSize(long size, int userId) { + setLong(Settings.Secure.LOCK_PATTERN_SIZE, size, userId); + } + + public void setVisibleDotsEnabled(boolean enabled, int userId) { + setBoolean(Settings.Secure.LOCK_DOTS_VISIBLE, enabled, userId); + } + + public boolean isVisibleDotsEnabled(int userId) { + return getBoolean(Settings.Secure.LOCK_DOTS_VISIBLE, true, userId); + } + + public void setShowErrorPath(boolean enabled, int userId) { + setBoolean(Settings.Secure.LOCK_SHOW_ERROR_PATH, enabled, userId); + } + + public boolean isShowErrorPath(int userId) { + return getBoolean(Settings.Secure.LOCK_SHOW_ERROR_PATH, true, userId); + } + /** * @param userId the user for which to report the value * @return Whether the lock screen is secured. @@ -1174,7 +1215,8 @@ public long getLockoutAttemptDeadline(int userId) { return deadline; } - private boolean getBoolean(String secureSettingKey, boolean defaultValue, int userId) { + /** @hide */ + protected boolean getBoolean(String secureSettingKey, boolean defaultValue, int userId) { try { return getLockSettings().getBoolean(secureSettingKey, defaultValue, userId); } catch (RemoteException re) { @@ -1182,7 +1224,8 @@ private boolean getBoolean(String secureSettingKey, boolean defaultValue, int us } } - private void setBoolean(String secureSettingKey, boolean enabled, int userId) { + /** @hide */ + protected void setBoolean(String secureSettingKey, boolean enabled, int userId) { try { getLockSettings().setBoolean(secureSettingKey, enabled, userId); } catch (RemoteException re) { @@ -1191,7 +1234,8 @@ private void setBoolean(String secureSettingKey, boolean enabled, int userId) { } } - private long getLong(String secureSettingKey, long defaultValue, int userHandle) { + /** @hide */ + protected long getLong(String secureSettingKey, long defaultValue, int userHandle) { try { return getLockSettings().getLong(secureSettingKey, defaultValue, userHandle); } catch (RemoteException re) { @@ -1199,8 +1243,9 @@ private long getLong(String secureSettingKey, long defaultValue, int userHandle) } } + /** @hide */ @UnsupportedAppUsage - private void setLong(String secureSettingKey, long value, int userHandle) { + protected void setLong(String secureSettingKey, long value, int userHandle) { try { getLockSettings().setLong(secureSettingKey, value, userHandle); } catch (RemoteException re) { @@ -1209,8 +1254,9 @@ private void setLong(String secureSettingKey, long value, int userHandle) { } } + /** @hide */ @UnsupportedAppUsage - private String getString(String secureSettingKey, int userHandle) { + protected String getString(String secureSettingKey, int userHandle) { try { return getLockSettings().getString(secureSettingKey, null, userHandle); } catch (RemoteException re) { @@ -1218,8 +1264,9 @@ private String getString(String secureSettingKey, int userHandle) { } } + /** @hide */ @UnsupportedAppUsage - private void setString(String secureSettingKey, String value, int userHandle) { + protected void setString(String secureSettingKey, String value, int userHandle) { try { getLockSettings().setString(secureSettingKey, value, userHandle); } catch (RemoteException re) { diff --git a/core/java/com/android/internal/widget/LockPatternView.java b/core/java/com/android/internal/widget/LockPatternView.java index dd31c388c9736..62998b21deec8 100644 --- a/core/java/com/android/internal/widget/LockPatternView.java +++ b/core/java/com/android/internal/widget/LockPatternView.java @@ -58,13 +58,14 @@ import com.android.internal.R; import com.android.internal.graphics.ColorUtils; +import com.android.internal.widget.LockPatternUtils; import java.util.ArrayList; import java.util.List; /** * Displays and detects the user's unlock attempt, which is a drag of a finger - * across 9 regions of the screen. + * across regions of the screen. * * Is also capable of displaying a static pattern in "in progress", "wrong" or * "correct" states. @@ -82,7 +83,7 @@ public class LockPatternView extends View { private static final int DOT_RADIUS_DECREASE_DURATION_MILLIS = 192; private static final int ALPHA_MAX_VALUE = 255; private static final float MIN_DOT_HIT_FACTOR = 0.2f; - private final CellState[][] mCellStates; + private CellState[][] mCellStates; private static final int CELL_ACTIVATE = 0; private static final int CELL_DEACTIVATE = 1; @@ -110,6 +111,8 @@ public class LockPatternView extends View { */ private static final int MILLIS_PER_CIRCLE_ANIMATING = 700; + private byte mPatternSize = LockPatternUtils.PATTERN_SIZE_DEFAULT; + /** * This can be used to avoid updating the display for very small motions or noisy panels. * It didn't seem to have much impact on the devices tested, so currently set to 0. @@ -122,7 +125,7 @@ public class LockPatternView extends View { private OnPatternListener mOnPatternListener; private ExternalHapticsPlayer mExternalHapticsPlayer; @UnsupportedAppUsage - private final ArrayList mPattern = new ArrayList(9); + private ArrayList mPattern = new ArrayList(mPatternSize * mPatternSize); /** * Lookup table for the circles of the pattern we are currently drawing. @@ -130,7 +133,7 @@ public class LockPatternView extends View { * in which case we use this to hold the cells we are drawing for the in * progress animation. */ - private final boolean[][] mPatternDrawLookup = new boolean[3][3]; + private boolean[][] mPatternDrawLookup = new boolean[mPatternSize][mPatternSize]; /** * the in progress point: @@ -141,7 +144,7 @@ public class LockPatternView extends View { private float mInProgressY = -1; private long mAnimatingPeriodStart; - private long[] mLineFadeStart = new long[9]; + private long[] mLineFadeStart = new long[mPatternSize * mPatternSize]; @UnsupportedAppUsage private DisplayMode mPatternDisplayMode = DisplayMode.Correct; @@ -151,6 +154,8 @@ public class LockPatternView extends View { @UnsupportedAppUsage private boolean mPatternInProgress = false; private boolean mFadePattern = true; + private boolean mVisibleDots = true; + private boolean mShowErrorPath = true; private boolean mFadeClear = false; private int mFadeAnimationAlpha = ALPHA_MAX_VALUE; @@ -186,8 +191,10 @@ public class LockPatternView extends View { private Drawable mNotSelectedDrawable; private boolean mUseLockPatternDrawable; + private LockPatternUtils mLockPatternUtils; + /** - * Represents a cell in the 3 X 3 matrix of the unlock pattern view. + * Represents a cell in the matrix of the unlock pattern view. */ public static final class Cell { @UnsupportedAppUsage @@ -195,25 +202,18 @@ public static final class Cell { @UnsupportedAppUsage final int column; - // keep # objects limited to 9 - private static final Cell[][] sCells = createCells(); - - private static Cell[][] createCells() { - Cell[][] res = new Cell[3][3]; - for (int i = 0; i < 3; i++) { - for (int j = 0; j < 3; j++) { - res[i][j] = new Cell(i, j); - } - } - return res; + static Cell[][] sCells; + static { + updateSize(LockPatternUtils.PATTERN_SIZE_DEFAULT); } /** * @param row The row of the cell. * @param column The column of the cell. + * @param size The size of the cell. */ - private Cell(int row, int column) { - checkRange(row, column); + private Cell(int row, int column, byte size) { + checkRange(row, column, size); this.row = row; this.column = column; } @@ -226,20 +226,28 @@ public int getColumn() { return column; } - public static Cell of(int row, int column) { - checkRange(row, column); + public static Cell of(int row, int column, byte size) { + checkRange(row, column, size); return sCells[row][column]; } - private static void checkRange(int row, int column) { - if (row < 0 || row > 2) { - throw new IllegalArgumentException("row must be in range 0-2"); - } - if (column < 0 || column > 2) { - throw new IllegalArgumentException("column must be in range 0-2"); + public static void updateSize(byte size) { + sCells = new Cell[size][size]; + for (int i = 0; i < size; i++) { + for (int j = 0; j < size; j++) { + sCells[i][j] = new Cell(i, j, size); + } } } + private static void checkRange(int row, int column, byte size) { + if (row < 0 || row > size - 1) { + throw new IllegalArgumentException("row must be in range 0-" + (size - 1)); + } + if (column < 0 || column > size - 1) { + throw new IllegalArgumentException("column must be in range 0-" + (size - 1)); + } + } @Override public String toString() { return "(row=" + row + ",clmn=" + column + ")"; @@ -314,8 +322,9 @@ public static interface OnPatternListener { /** * A pattern was detected from the user. * @param pattern The pattern. + * @param patternSize The pattern size. */ - void onPatternDetected(List pattern); + void onPatternDetected(List pattern, byte patternSize); } /** An external haptics player for pattern updates. */ @@ -398,9 +407,9 @@ public LockPatternView(Context context, AttributeSet attrs) { mPaint.setAntiAlias(true); mPaint.setDither(true); - mCellStates = new CellState[3][3]; - for (int i = 0; i < 3; i++) { - for (int j = 0; j < 3; j++) { + mCellStates = new CellState[mPatternSize][mPatternSize]; + for (int i = 0; i < mPatternSize; i++) { + for (int j = 0; j < mPatternSize; j++) { mCellStates[i][j] = new CellState(); mCellStates[i][j].radius = mDotSize/2; mCellStates[i][j].row = i; @@ -439,6 +448,13 @@ public boolean isInStealthMode() { return mInStealthMode; } + /** + * @return the current pattern lockscreen size. + */ + public byte getLockPatternSize() { + return mPatternSize; + } + /** * Set whether the view is in stealth mode. If true, there will be no * visible feedback as the user enters the pattern. @@ -450,6 +466,22 @@ public void setInStealthMode(boolean inStealthMode) { mInStealthMode = inStealthMode; } + public void setVisibleDots(boolean visibleDots) { + mVisibleDots = visibleDots; + } + + public boolean isVisibleDots() { + return mVisibleDots; + } + + public void setShowErrorPath(boolean showErrorPath) { + mShowErrorPath = showErrorPath; + } + + public boolean isShowErrorPath() { + return mShowErrorPath; + } + /** * Set whether the pattern should fade as it's being drawn. If * true, each segment of the pattern fades over time. @@ -458,6 +490,36 @@ public void setFadePattern(boolean fadePattern) { mFadePattern = fadePattern; } + /** + * Set the pattern size of the lockscreen + * + * @param size The pattern size. + */ + public void setLockPatternSize(byte size) { + mPatternSize = size; + Cell.updateSize(size); + mCellStates = new CellState[mPatternSize][mPatternSize]; + for (int i = 0; i < mPatternSize; i++) { + for (int j = 0; j < mPatternSize; j++) { + mCellStates[i][j] = new CellState(); + mCellStates[i][j].radius = mDotSize / 2; + mCellStates[i][j].row = i; + mCellStates[i][j].col = j; + } + } + mPattern = new ArrayList(size * size); + mLineFadeStart = new long[size * size]; + mPatternDrawLookup = new boolean[size][size]; + } + + /** + * Set the LockPatternUtil instance used to encode a pattern to a string + * @param utils The instance. + */ + public void setLockPatternUtils(LockPatternUtils utils) { + mLockPatternUtils = utils; + } + /** * Set the call back for pattern detection. * @param onPatternListener The call back. @@ -513,6 +575,8 @@ public void setDisplayMode(DisplayMode displayMode) { mInProgressX = getCenterXForColumn(first.getColumn()); mInProgressY = getCenterYForRow(first.getRow()); clearPatternDrawLookup(); + } else if (displayMode == DisplayMode.Wrong && !mShowErrorPath) { + resetPatternCellSize(); } invalidate(); } @@ -642,7 +706,7 @@ private void notifyPatternStarted() { @UnsupportedAppUsage private void notifyPatternDetected() { if (mOnPatternListener != null) { - mOnPatternListener.onPatternDetected(mPattern); + mOnPatternListener.onPatternDetected(mPattern, mPatternSize); } } @@ -720,10 +784,10 @@ public boolean isEmpty() { * the next attempt. */ private void clearPatternDrawLookup() { - for (int i = 0; i < 3; i++) { - for (int j = 0; j < 3; j++) { + for (int i = 0; i < mPatternSize; i++) { + for (int j = 0; j < mPatternSize; j++) { mPatternDrawLookup[i][j] = false; - mLineFadeStart[i+j*3] = 0; + mLineFadeStart[i * mPatternSize + j] = 0; } } } @@ -748,11 +812,11 @@ public void enableInput() { @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { final int width = w - mPaddingLeft - mPaddingRight; - mSquareWidth = width / 3.0f; + mSquareWidth = width / (float) mPatternSize; if (DEBUG_A11Y) Log.v(TAG, "onSizeChanged(" + w + "," + h + ")"); final int height = h - mPaddingTop - mPaddingBottom; - mSquareHeight = height / 3.0f; + mSquareHeight = height / (float) mPatternSize; mExploreByTouchHelper.invalidateRoot(); mDotHitMaxRadius = Math.min(mSquareHeight / 2, mSquareWidth / 2); mDotHitRadius = mDotHitMaxRadius * mDotHitFactor; @@ -815,7 +879,6 @@ private Cell detectAndAddHit(float x, float y) { if (cell != null) { // check for gaps in existing pattern - Cell fillInGapCell = null; final ArrayList pattern = mPattern; Cell lastCell = null; if (!pattern.isEmpty()) { @@ -826,33 +889,19 @@ private Cell detectAndAddHit(float x, float y) { int fillInRow = lastCell.row; int fillInColumn = lastCell.column; - if (Math.abs(dRow) == 2 && Math.abs(dColumn) != 1) { - fillInRow = lastCell.row + ((dRow > 0) ? 1 : -1); - } - - if (Math.abs(dColumn) == 2 && Math.abs(dRow) != 1) { - fillInColumn = lastCell.column + ((dColumn > 0) ? 1 : -1); - } - - fillInGapCell = Cell.of(fillInRow, fillInColumn); - } - - if (fillInGapCell != null && - !mPatternDrawLookup[fillInGapCell.row][fillInGapCell.column]) { - addCellToPattern(fillInGapCell); - if (mKeepDotActivated) { - if (mFadePattern) { - startCellDeactivatedAnimation(fillInGapCell, /* fillInGap= */ true); - } else { - startCellActivatedAnimation(fillInGapCell); + if (dRow == 0 || dColumn == 0 || Math.abs(dRow) == Math.abs(dColumn)) { + while (true) { + fillInRow += Integer.signum(dRow); + fillInColumn += Integer.signum(dColumn); + if (fillInRow == cell.row && fillInColumn == cell.column) break; + Cell fillInGapCell = Cell.of(fillInRow, fillInColumn, mPatternSize); + if (!mPatternDrawLookup[fillInGapCell.row][fillInGapCell.column]) { + addCellToPattern(fillInGapCell); + } } } } - if (mKeepDotActivated && lastCell != null) { - startCellDeactivatedAnimation(lastCell, /* fillInGap= */ false); - } - addCellToPattern(cell); performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY, HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING); @@ -1083,8 +1132,8 @@ private Cell checkForNewHit(float x, float y) { /** Helper method to find which cell a point maps to. */ @Nullable private Cell detectCellHit(float x, float y) { - for (int row = 0; row < 3; row++) { - for (int column = 0; column < 3; column++) { + for (int row = 0; row < mPatternSize; row++) { + for (int column = 0; column < mPatternSize; column++) { float centerY = getCenterYForRow(row); float centerX = getCenterXForColumn(column); float hitRadiusSquared; @@ -1103,7 +1152,7 @@ private Cell detectCellHit(float x, float y) { if ((x - centerX) * (x - centerX) + (y - centerY) * (y - centerY) < hitRadiusSquared) { - return Cell.of(row, column); + return Cell.of(row, column, mPatternSize); } } } @@ -1252,11 +1301,6 @@ private void handleActionUp() { cancelLineAnimations(); } notifyPatternDetected(); - // Also clear pattern if fading is enabled - if (mFadePattern) { - clearPatternDrawLookup(); - mPatternDisplayMode = DisplayMode.Correct; - } invalidate(); } if (PROFILE_DRAWING) { @@ -1273,8 +1317,8 @@ private void deactivateLastCell() { } private void cancelLineAnimations() { - for (int i = 0; i < 3; i++) { - for (int j = 0; j < 3; j++) { + for (int i = 0; i < mPatternSize; i++) { + for (int j = 0; j < mPatternSize; j++) { CellState state = mCellStates[i][j]; if (state.activationAnimator != null) { state.activationAnimator.cancel(); @@ -1428,7 +1472,9 @@ protected void onDraw(Canvas canvas) { // TODO: the path should be created and cached every time we hit-detect a cell // only the last segment of the path should be computed here // draw the path of the pattern (unless we are in stealth mode) - final boolean drawPath = !mInStealthMode; + final boolean drawWrongPath = mPatternDisplayMode == DisplayMode.Wrong && mShowErrorPath; + final boolean drawPath = (!mInStealthMode && mPatternDisplayMode != DisplayMode.Wrong) + || drawWrongPath; if (drawPath && !mFadeClear) { mPathPaint.setColor(getCurrentColor(true /* partOfPattern */)); @@ -1497,24 +1543,26 @@ protected void onDraw(Canvas canvas) { } // draw the circles - for (int i = 0; i < 3; i++) { - float centerY = getCenterYForRow(i); - for (int j = 0; j < 3; j++) { - CellState cellState = mCellStates[i][j]; - float centerX = getCenterXForColumn(j); - float translationY = cellState.translationY; - - if (mUseLockPatternDrawable) { - drawCellDrawable(canvas, i, j, cellState.radius, drawLookup[i][j]); - } else { - if (isHardwareAccelerated() && cellState.hwAnimating) { - RecordingCanvas recordingCanvas = (RecordingCanvas) canvas; - recordingCanvas.drawCircle(cellState.hwCenterX, cellState.hwCenterY, - cellState.hwRadius, cellState.hwPaint); + if (mVisibleDots) { + for (int i = 0; i < mPatternSize; i++) { + float centerY = getCenterYForRow(i); + for (int j = 0; j < mPatternSize; j++) { + CellState cellState = mCellStates[i][j]; + float centerX = getCenterXForColumn(j); + float translationY = cellState.translationY; + + if (mUseLockPatternDrawable) { + drawCellDrawable(canvas, i, j, cellState.radius, drawLookup[i][j]); } else { - drawCircle(canvas, (int) centerX, (int) centerY + translationY, - cellState.radius, drawLookup[i][j], cellState.alpha, - cellState.activationAnimationProgress); + if (isHardwareAccelerated() && cellState.hwAnimating) { + RecordingCanvas recordingCanvas = (RecordingCanvas) canvas; + recordingCanvas.drawCircle(cellState.hwCenterX, cellState.hwCenterY, + cellState.hwRadius, cellState.hwPaint); + } else { + drawCircle(canvas, (int) centerX, (int) centerY + translationY, + cellState.radius, drawLookup[i][j], cellState.alpha, + cellState.activationAnimationProgress); + } } } } @@ -1524,7 +1572,8 @@ protected void onDraw(Canvas canvas) { private void drawLineSegment(Canvas canvas, float startX, float startY, float endX, float endY, long lineFadeStart, long elapsedRealtime) { float fadeAwayProgress; - if (mFadePattern) { + final boolean drawWrongPath = mPatternDisplayMode == DisplayMode.Wrong && mShowErrorPath; + if (mFadePattern && !drawWrongPath) { if (elapsedRealtime - lineFadeStart >= mLineFadeOutAnimationDelayMs + mLineFadeOutAnimationDurationMs) { // Time for this segment animation is out so we don't need to draw it. @@ -1596,7 +1645,10 @@ private int getDotColor() { } private int getCurrentColor(boolean partOfPattern) { - if (!partOfPattern || mInStealthMode || mPatternInProgress) { + if (!partOfPattern + || (mInStealthMode && mPatternDisplayMode != DisplayMode.Wrong) + || (mPatternDisplayMode == DisplayMode.Wrong && !mShowErrorPath) + || mPatternInProgress) { // unselected circle return mRegularColor; } else if (mPatternDisplayMode == DisplayMode.Wrong) { @@ -1615,7 +1667,8 @@ private int getCurrentColor(boolean partOfPattern) { */ private void drawCircle(Canvas canvas, float centerX, float centerY, float radius, boolean partOfPattern, float alpha, float activationAnimationProgress) { - if (mFadePattern && !mInStealthMode) { + final boolean drawWrongPath = mPatternDisplayMode == DisplayMode.Wrong && mShowErrorPath; + if (mFadePattern && !mInStealthMode && !drawWrongPath) { int resultColor = ColorUtils.blendARGB(mDotColor, mDotActivatedColor, /* ratio= */ activationAnimationProgress); mPaint.setColor(resultColor); @@ -1655,12 +1708,13 @@ private void drawCellDrawable(Canvas canvas, int i, int j, float radius, @Override protected Parcelable onSaveInstanceState() { Parcelable superState = super.onSaveInstanceState(); - byte[] patternBytes = LockPatternUtils.patternToByteArray(mPattern); + byte[] patternBytes = LockPatternUtils.patternToByteArray(mPattern, mPatternSize); String patternString = patternBytes != null ? new String(patternBytes) : null; return new SavedState(superState, patternString, mPatternDisplayMode.ordinal(), - mInputEnabled, mInStealthMode); + mPatternSize, mInputEnabled, mInStealthMode, + mVisibleDots, mShowErrorPath); } @Override @@ -1669,10 +1723,14 @@ protected void onRestoreInstanceState(Parcelable state) { super.onRestoreInstanceState(ss.getSuperState()); setPattern( DisplayMode.Correct, - LockPatternUtils.byteArrayToPattern(ss.getSerializedPattern().getBytes())); + LockPatternUtils.byteArrayToPattern(ss.getSerializedPattern().getBytes(), + ss.getPatternSize())); mPatternDisplayMode = DisplayMode.values()[ss.getDisplayMode()]; + mPatternSize = ss.getPatternSize(); mInputEnabled = ss.isInputEnabled(); mInStealthMode = ss.isInStealthMode(); + mVisibleDots = ss.isVisibleDots(); + mShowErrorPath = ss.isShowErrorPath(); } @Override @@ -1689,20 +1747,27 @@ private static class SavedState extends BaseSavedState { private final String mSerializedPattern; private final int mDisplayMode; + private final byte mPatternSize; private final boolean mInputEnabled; private final boolean mInStealthMode; + private final boolean mVisibleDots; + private final boolean mShowErrorPath; /** * Constructor called from {@link LockPatternView#onSaveInstanceState()} */ @UnsupportedAppUsage private SavedState(Parcelable superState, String serializedPattern, int displayMode, - boolean inputEnabled, boolean inStealthMode) { + byte patternSize, boolean inputEnabled, boolean inStealthMode, + boolean visibleDots, boolean showErrorPath) { super(superState); mSerializedPattern = serializedPattern; mDisplayMode = displayMode; + mPatternSize = patternSize; mInputEnabled = inputEnabled; mInStealthMode = inStealthMode; + mVisibleDots = visibleDots; + mShowErrorPath = showErrorPath; } /** @@ -1713,8 +1778,11 @@ private SavedState(Parcel in) { super(in); mSerializedPattern = in.readString(); mDisplayMode = in.readInt(); + mPatternSize = (byte) in.readByte(); mInputEnabled = (Boolean) in.readValue(null); mInStealthMode = (Boolean) in.readValue(null); + mVisibleDots = (Boolean) in.readValue(null); + mShowErrorPath = (Boolean) in.readValue(null); } public String getSerializedPattern() { @@ -1725,6 +1793,10 @@ public int getDisplayMode() { return mDisplayMode; } + public byte getPatternSize() { + return mPatternSize; + } + public boolean isInputEnabled() { return mInputEnabled; } @@ -1733,13 +1805,24 @@ public boolean isInStealthMode() { return mInStealthMode; } + public boolean isVisibleDots() { + return mVisibleDots; + } + + public boolean isShowErrorPath() { + return mShowErrorPath; + } + @Override public void writeToParcel(Parcel dest, int flags) { super.writeToParcel(dest, flags); dest.writeString(mSerializedPattern); dest.writeInt(mDisplayMode); + dest.writeByte(mPatternSize); dest.writeValue(mInputEnabled); dest.writeValue(mInStealthMode); + dest.writeValue(mVisibleDots); + dest.writeValue(mShowErrorPath); } @SuppressWarnings({ "unused", "hiding" }) // Found using reflection diff --git a/core/java/com/android/internal/widget/LockscreenCredential.java b/core/java/com/android/internal/widget/LockscreenCredential.java index 3d9f0d2041592..573af88fe3309 100644 --- a/core/java/com/android/internal/widget/LockscreenCredential.java +++ b/core/java/com/android/internal/widget/LockscreenCredential.java @@ -135,10 +135,11 @@ public static LockscreenCredential createNone() { /** * Creates a LockscreenCredential object representing the given pattern. */ - public static LockscreenCredential createPattern(@NonNull List pattern) { + public static LockscreenCredential createPattern(@NonNull List pattern, + byte gridSize) { return new LockscreenCredential( CREDENTIAL_TYPE_PATTERN, - LockPatternUtils.patternToByteArray(pattern), + LockPatternUtils.patternToByteArray(pattern, gridSize), /* hasInvalidChars= */ false, /* isUnifiedProfilePassword= */ false); } diff --git a/core/java/com/android/server/LocalServices.java b/core/java/com/android/server/LocalServices.java index 2a10918a51619..4f9cc88aacdc3 100644 --- a/core/java/com/android/server/LocalServices.java +++ b/core/java/com/android/server/LocalServices.java @@ -18,7 +18,7 @@ import com.android.internal.annotations.VisibleForTesting; -import android.util.ArrayMap; +import java.util.HashMap; /** * This class is used in a similar way as ServiceManager, except the services registered here @@ -33,8 +33,8 @@ public final class LocalServices { private LocalServices() {} - private static final ArrayMap, Object> sLocalServiceObjects = - new ArrayMap, Object>(); + private static final HashMap, Object> sLocalServiceObjects = + new HashMap, Object>(); /** * Returns a local service instance that implements the specified interface. diff --git a/core/java/com/android/server/backup/NetworkPolicyBackupHelper.java b/core/java/com/android/server/backup/NetworkPolicyBackupHelper.java new file mode 100644 index 0000000000000..1d17aca01247b --- /dev/null +++ b/core/java/com/android/server/backup/NetworkPolicyBackupHelper.java @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2021 The Calyx Institute + * + * 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.backup; + +import android.app.backup.BlobBackupHelper; +import android.content.Context; +import android.net.INetworkPolicyManager; +import android.os.ServiceManager; +import android.util.Log; +import android.util.Slog; + +public class NetworkPolicyBackupHelper extends BlobBackupHelper { + private static final String TAG = "NetworkPolicyBackupHelper"; + private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + + // Current version of the blob schema + static final int BLOB_VERSION = 1; + + // Key under which the payload blob is stored + static final String KEY_NETWORK_POLICY = "network_policy"; + + private final int mUserId; + + public NetworkPolicyBackupHelper(int userId) { + super(BLOB_VERSION, KEY_NETWORK_POLICY); + mUserId = userId; + } + + @Override + protected byte[] getBackupPayload(String key) { + byte[] newPayload = null; + if (KEY_NETWORK_POLICY.equals(key)) { + try { + INetworkPolicyManager npm = INetworkPolicyManager.Stub.asInterface( + ServiceManager.getService(Context.NETWORK_POLICY_SERVICE)); + newPayload = npm.getBackupPayload(mUserId); + } catch (Exception e) { + // Treat as no data + Slog.e(TAG, "Couldn't communicate with network policy manager", e); + newPayload = null; + } + } + return newPayload; + } + + @Override + protected void applyRestoredPayload(String key, byte[] payload) { + if (DEBUG) { + Slog.v(TAG, "Got restore of " + key); + } + + if (KEY_NETWORK_POLICY.equals(key)) { + try { + INetworkPolicyManager.Stub.asInterface( + ServiceManager.getService(Context.NETWORK_POLICY_SERVICE)) + .applyRestore(payload, mUserId); + } catch (Exception e) { + Slog.e(TAG, "Couldn't communicate with network policy manager", e); + } + } + } +} diff --git a/core/java/com/nvidia/NvAppProfileService.java b/core/java/com/nvidia/NvAppProfileService.java new file mode 100644 index 0000000000000..ede1391b8497f --- /dev/null +++ b/core/java/com/nvidia/NvAppProfileService.java @@ -0,0 +1,263 @@ +/* + * Copyright (c) 2012 - 2014 NVIDIA Corporation. All rights reserved. + * + * 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. + * + * Class structure based upon Camera in Camera.java: + * Copyright (C) 2009 The Android Open Source Project + */ + +package com.nvidia; + +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.res.Resources; +import android.graphics.drawable.Drawable; +import android.os.SystemProperties; +import android.os.UserHandle; +import android.text.TextUtils; +import android.util.Log; + +import com.nvidia.profilemanager.NvAppProfileSettingId; + +import java.util.Arrays; +import java.util.List; + +/** + * @hide + */ +public class NvAppProfileService { + private static final String TAG = "NvAppProfileService"; + private static final String APP_START_ACTION = + "com.nvidia.NvAppProfileService.action.APP_START"; + private static final String APP_START_TARGET_PACKAGE = "com.nvidia.stats"; + private static final String FEATURE_POWER_BUDGET_CONTROL = + "nvidia.feature.power_budget_control"; + private static final String FEATURE_FAN_ON_DEVICE = "nvidia.feature.fan_on_device"; + private static final String NvPowerModeProperty = "persist.vendor.sys.NV_POWER_MODE"; + + private final NvAppProfiles mAppProfile; + private final Context mContext; + + private boolean mInitAppProfiles = false; + private boolean mFanCapEnabled = false; + private boolean mPbcEnabled = false; + + public NvAppProfileService(Context context) { + Context appContext = context.getApplicationContext(); + if (appContext == null) { + mContext = context; + } else { + mContext = appContext; + } + + mAppProfile = new NvAppProfiles(mContext); + } + + private static String getPackageName(String appName) { + int index = appName.indexOf('/'); + if (index < 0) { + Log.e(TAG, "appName does not contain '/'. " + + "The packageName cannot be extracted from appName!"); + return null; + } + return appName.substring(0, index); + } + + /* + * These are functions that depend on NvAppProfiles and may or may not + * be supported for certain platforms. In the latter case, these methods + * should return -1. + */ + public boolean canForceHwUi(String appName) { + if (appName == null) { + return false; + } + + String packageName = getPackageName(appName); + if (packageName == null && mAppProfile.getApplicationProfile(packageName, + NvAppProfileSettingId.FORCE_HW_UI) <= 0) { + return false; + } + + return true; + } + + public boolean getAppProfileFRCEnable(String packageName) { + return packageName != null && mAppProfile.getApplicationProfile(packageName, + NvAppProfileSettingId.VIDEO_FRC_ENABLE) == 1; + } + + public boolean getAppProfileCreateSecureDecoder(String packageName) { + return packageName != null && mAppProfile.getApplicationProfile(packageName, + NvAppProfileSettingId.VIDEO_SECURE_DECODE) == 1; + } + + public boolean getAppProfileTSFilterEnable(String packageName) { + return packageName != null && mAppProfile.getApplicationProfile(packageName, + NvAppProfileSettingId.VIDEO_TS_FILTERING) == 1; + } + + public boolean getAppProfileNvidiaCertification(String packageName) { + return packageName != null && mAppProfile.getApplicationProfile(packageName, + NvAppProfileSettingId.NVIDIA_VIDEO_CERTIFICATION_ENABLED) == 1; + } + + public boolean getAppProfileDisableApp(String packageName) { + return packageName != null && mAppProfile.getApplicationProfile(packageName, + NvAppProfileSettingId.DISABLE_APP) == 1; + } + + private int getAppProfileCpuScalingMinFreq(String packageName) { + return mAppProfile.getApplicationProfile(packageName, + NvAppProfileSettingId.SCALING_MIN_FREQ); + } + + private int getAppProfileCpuCoreBias(String packageName) { + return mAppProfile.getApplicationProfile(packageName, NvAppProfileSettingId.CORE_BIAS); + } + + private int getAppProfileGpuScaling(String packageName) { + return mAppProfile.getApplicationProfile(packageName, + NvAppProfileSettingId.GPU_SCALING); + } + + private int getAppProfileCpuMaxNormalFreq(String packageName) { + return mAppProfile.getApplicationProfile(packageName, + NvAppProfileSettingId.CPU_FREQ_BIAS); + } + + private int getAppProfileCpuMaxNormalFreqPercent(String packageName) { + return mAppProfile.getApplicationProfile(packageName, + NvAppProfileSettingId.MAX_CPU_FREQ_PCT); + } + + private int getAppProfileCpuMinCore(String packageName) { + return mAppProfile.getApplicationProfile(packageName, + NvAppProfileSettingId.MIN_CPU_CORES); + } + + private int getAppProfileCpuMaxCore(String packageName) { + return mAppProfile.getApplicationProfile(packageName, + NvAppProfileSettingId.MAX_CPU_CORES); + } + + private int getAppProfileGpuCbusCapLevel(String packageName) { + return mAppProfile.getApplicationProfile(packageName, + NvAppProfileSettingId.GPU_CORE_CAP); + } + + private int getAppProfileEdpMode(String packageName) { + return mAppProfile.getApplicationProfile(packageName, NvAppProfileSettingId.EDP_MODE); + } + + private int getAppProfilePbcPwr(String packageName) { + if (!mPbcEnabled) return -1; + + return mAppProfile.getApplicationProfile(packageName, + NvAppProfileSettingId.PBC_PWR_LIMIT); + } + + private int getAppProfileFanCap(String packageName) { + if (!mFanCapEnabled) return -1; + + return mAppProfile.getApplicationProfile(packageName, + NvAppProfileSettingId.FAN_PWM_CAP); + } + + private int getAppProfileVoltTempMode(String packageName) { + return mAppProfile.getApplicationProfile(packageName, + NvAppProfileSettingId.VOLT_TEMP_MODE); + } + + private int getAppProfileAggresivePrismEnable(String packageName) { + return mAppProfile.getApplicationProfile(packageName, + NvAppProfileSettingId.AGGRESSIVE_PRISM_ENABLE); + } + + private int getAppProfileDevicePowerMode(String packageName) { + return mAppProfile.getApplicationProfile(packageName, + NvAppProfileSettingId.SYSTEM_POWER_MODE); + } + + public String getAppProfileRegionEnableList(String packageName) { + return mAppProfile.getApplicationProfileString(packageName, + NvAppProfileSettingId.SET_REGION_LIST); + } + + public int getAppProfileNvidiaBBCApps(String packageName) { + return mAppProfile.getApplicationProfile(packageName, NvAppProfileSettingId.BBC_APPS); + } + + public List getAppProfileForceQueryAppList() { + String applicationProfileString = mAppProfile.getApplicationProfileString( + "com.nvidia.shield.force_query_app_list", + NvAppProfileSettingId.FORCE_QUERY_PACKAGES); + if (TextUtils.isEmpty(applicationProfileString)) { + return null; + } + return Arrays.asList(applicationProfileString.split(";")); + } + + public List getAppProfileAutoMediaScanPackages() { + String applicationProfileString = mAppProfile.getApplicationProfileString( + "com.nvidia.shield.auto_media_scan_packages", + NvAppProfileSettingId.AUTO_MEDIA_SCAN_PACKAGES); + if (TextUtils.isEmpty(applicationProfileString)) { + return null; + } + return Arrays.asList(applicationProfileString.split(";")); + } + + private int retrievePowerMode() { + final String powerMode = SystemProperties.get(NvPowerModeProperty); + if (powerMode != null) { + try { + return Integer.parseInt(powerMode); + } catch (NumberFormatException ex) { + // Fallthrough to error case + } + } + + return -1; + } + + private void setGpuModeSetting(String packageName) { + mAppProfile.getApplicationProfile(packageName, + NvAppProfileSettingId.GPU_MODESET_ENABLE); + } + + /** + * Interface for the caller + */ + public void setAppProfile(String packageName) { + // Greedy initialization of App Profiles + if (!mInitAppProfiles) { + PackageManager pm = mContext.getPackageManager(); + mPbcEnabled = pm.hasSystemFeature(FEATURE_POWER_BUDGET_CONTROL); + mFanCapEnabled = pm.hasSystemFeature(FEATURE_FAN_ON_DEVICE); + + Log.w(TAG, "Enabled"); + mInitAppProfiles = true; + } + + mAppProfile.powerHint(packageName); + + Intent intent = new Intent(APP_START_ACTION); + intent.setPackage(APP_START_TARGET_PACKAGE); + intent.putExtra("AppStartId", packageName); + mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT, + "nvidia.permission.READ_APP_START_INFO"); + } +} diff --git a/core/java/com/nvidia/NvAppProfiles.java b/core/java/com/nvidia/NvAppProfiles.java new file mode 100644 index 0000000000000..64c38aa19258c --- /dev/null +++ b/core/java/com/nvidia/NvAppProfiles.java @@ -0,0 +1,77 @@ +package com.nvidia; + +import android.content.Context; +import android.content.Intent; +import android.os.IBinder; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.util.Log; + +import com.nvidia.NvCPLSvc.INvCPLRemoteService; + +public class NvAppProfiles { + /** + * Unique name used for NvCPLSvc to whitelist this class + */ + static final String NV_APP_PROFILES_NAME = "Frameworks_NvAppProfiles"; + static final boolean DEBUG = false; + private static final String TAG = "NvAppProfiles"; + private final Context mContext; + private INvCPLRemoteService mNvCPLSvc = null; + + /** + * Callback class given by the NvCPLService + */ + + public NvAppProfiles(Context context) { + mContext = context; + } + + public int getApplicationProfile(String packageName, int settingId) { + getNvCPLService(); + if (mNvCPLSvc != null) { + try { + return mNvCPLSvc.getAppProfileSettingInt(packageName, settingId); + } catch (RemoteException ex) { + Log.w(TAG, "Failed to retrieve profile setting. Error=" + ex.getMessage()); + } + } + + return -1; + } + + public String getApplicationProfileString(String packageName, int settingId) { + getNvCPLService(); + if (mNvCPLSvc != null) { + try { + return mNvCPLSvc.getAppProfileSettingString(packageName, settingId); + } catch (RemoteException ex) { + Log.w(TAG, "Failed to retrieve profile setting. Error=" + ex.getMessage()); + } + } + + return null; + } + + public void powerHint(String packageName) { + getNvCPLService(); + if (mNvCPLSvc != null) { + try { + mNvCPLSvc.powerHint(packageName); + } catch (RemoteException ex) { + Log.w(TAG, "Failed to send power hint. Error=" + ex.getMessage()); + } + } + } + + private void getNvCPLService() { + if (mNvCPLSvc == null) { + try { + mNvCPLSvc = INvCPLRemoteService.Stub.asInterface( + ServiceManager.getService("nvcpl")); + } catch (Exception e) { + Log.e(TAG, "Failed to bind to service. " + e.getMessage()); + } + } + } +} diff --git a/core/java/com/nvidia/NvCPLSvc/INvCPLRemoteService.aidl b/core/java/com/nvidia/NvCPLSvc/INvCPLRemoteService.aidl new file mode 100644 index 0000000000000..65ce84b5de291 --- /dev/null +++ b/core/java/com/nvidia/NvCPLSvc/INvCPLRemoteService.aidl @@ -0,0 +1,27 @@ +package com.nvidia.NvCPLSvc; + +import android.content.Intent; +import java.util.List; + +import com.nvidia.NvCPLSvc.NvAppProfile; +import com.nvidia.NvCPLSvc.NvSaverAppInfo; + +/** @hide */ +interface INvCPLRemoteService { + IBinder getToolsApiInterface(String str); + String getAppProfileSettingString(String pkgName, int settingId); + int getAppProfileSettingInt(String pkgName, int settingId); + int getAppProfileSettingBoolean(String pkgName, int settingId); + byte[] getAppProfileSetting3DVStruct(String pkgName); + void handleIntent(in Intent intent); + boolean setNvSaverAppInfo(String pkgName, int list); + boolean setNvSaverAppInfoAll(in List appList); + List getNvSaverAppInfo(int i); + boolean setAppProfileSetting(String packageName, int typeId, int settingId, String value); + int getActiveProfileType(String packageName); + int[] getProfileTypes(String str); + boolean setActiveProfileType(String packageName, int typeId); + NvAppProfile[] getAppProfiles(in String[] strArr); + String getDeviceSerial(); + void powerHint(String str); +} diff --git a/core/java/com/nvidia/NvCPLSvc/NvAppProfile.aidl b/core/java/com/nvidia/NvCPLSvc/NvAppProfile.aidl new file mode 100644 index 0000000000000..a302010968a67 --- /dev/null +++ b/core/java/com/nvidia/NvCPLSvc/NvAppProfile.aidl @@ -0,0 +1,2 @@ +package com.nvidia.NvCPLSvc; +parcelable NvAppProfile; diff --git a/core/java/com/nvidia/NvCPLSvc/NvAppProfile.java b/core/java/com/nvidia/NvCPLSvc/NvAppProfile.java new file mode 100644 index 0000000000000..1486edbe45c15 --- /dev/null +++ b/core/java/com/nvidia/NvCPLSvc/NvAppProfile.java @@ -0,0 +1,65 @@ +package com.nvidia.NvCPLSvc; + +import android.net.ProxyInfo; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.SparseArray; + +public class NvAppProfile implements Parcelable { + public static final Creator CREATOR = new Creator() { + public NvAppProfile createFromParcel(Parcel parcel) { + return NvAppProfile.createFromParcel(parcel); + } + + public NvAppProfile[] newArray(int size) { + return new NvAppProfile[size]; + } + }; + public final String pkgName; + public final String pkgVersion; + public final int typeId; + public SparseArray settings; + + public NvAppProfile(int typeId, String pkgName, String pkgVersion, + SparseArray settings) { + this.typeId = typeId; + this.pkgName = pkgName; + this.pkgVersion = pkgVersion; + this.settings = settings; + } + + private static NvAppProfile createFromParcel(Parcel parcel) { + int typeId = parcel.readInt(); + String pkgName = decodeNull(parcel.readString()); + String pkgVersion = decodeNull(parcel.readString()); + int numSettings = parcel.readInt(); + SparseArray settings = new SparseArray(); + for (int i = 0; i < numSettings; i++) { + settings.append(parcel.readInt(), parcel.readString()); + } + return new NvAppProfile(typeId, pkgName, pkgVersion, settings); + } + + private static String encodeNull(String string) { + return string != null ? string : ""; + } + + private static String decodeNull(String string) { + return !string.equals("") ? string : null; + } + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel parcel, int flag) { + parcel.writeInt(this.typeId); + parcel.writeString(encodeNull(this.pkgName)); + parcel.writeString(encodeNull(this.pkgVersion)); + parcel.writeInt(this.settings.size()); + for (int i = 0; i < this.settings.size(); i++) { + parcel.writeInt(this.settings.keyAt(i)); + parcel.writeString((String) this.settings.valueAt(i)); + } + } +} diff --git a/core/java/com/nvidia/NvCPLSvc/NvSaverAppInfo.aidl b/core/java/com/nvidia/NvCPLSvc/NvSaverAppInfo.aidl new file mode 100644 index 0000000000000..ed0dd7d692c7f --- /dev/null +++ b/core/java/com/nvidia/NvCPLSvc/NvSaverAppInfo.aidl @@ -0,0 +1,2 @@ +package com.nvidia.NvCPLSvc; +parcelable NvSaverAppInfo; diff --git a/core/java/com/nvidia/NvCPLSvc/NvSaverAppInfo.java b/core/java/com/nvidia/NvCPLSvc/NvSaverAppInfo.java new file mode 100644 index 0000000000000..76f1f05d618fb --- /dev/null +++ b/core/java/com/nvidia/NvCPLSvc/NvSaverAppInfo.java @@ -0,0 +1,166 @@ +package com.nvidia.NvCPLSvc; + +import android.graphics.drawable.Drawable; +import android.os.Parcel; +import android.os.Parcelable; + +public class NvSaverAppInfo implements Parcelable { + public static final Creator CREATOR = new Creator() { + public NvSaverAppInfo createFromParcel(Parcel source) { + return new NvSaverAppInfo(source); + } + + public NvSaverAppInfo[] newArray(int size) { + return new NvSaverAppInfo[size]; + } + }; + + public static final int NVSAVER_ACTIVITY_HIGH = 1; + public static final int NVSAVER_ACTIVITY_LOW = 3; + public static final int NVSAVER_ACTIVITY_MIDIUM = 2; + public static final int NVSAVER_LIST_BLACKLIST = 3; + public static final int NVSAVER_LIST_NONE = 1; + public static final int NVSAVER_LIST_WHITELIST = 2; + public static final int NV_APP_OPTIMIZE_LIST = 4; + public int mAppList; + public String mPkgName; + public long mTotalWakeupStatsTime; + public int mUid; + public long mWakeupStatsTime; + public int mWakeupTimes; + public int mWowWakeupTimes; + private int mAppActivity; + private Drawable mAppIcon; + private String mAppLabel; + private float mPowerSaver; + + public NvSaverAppInfo(Parcel pl) { + mUid = pl.readInt(); + mAppList = pl.readInt(); + mWakeupTimes = pl.readInt(); + mWowWakeupTimes = pl.readInt(); + mPkgName = pl.readString(); + mWakeupStatsTime = pl.readLong(); + mTotalWakeupStatsTime = pl.readLong(); + mAppLabel = null; + mAppIcon = null; + mAppActivity = 0; + mPowerSaver = 0.0f; + } + + public NvSaverAppInfo(int u, int a, int w, int wow, String pkg, long t1, long t2) { + mUid = u; + mAppList = a; + mWakeupTimes = w; + mWowWakeupTimes = wow; + mPkgName = pkg; + mWakeupStatsTime = t1; + mTotalWakeupStatsTime = t2; + mAppLabel = null; + mAppIcon = null; + mAppActivity = 0; + mPowerSaver = 0.0f; + } + + public String getAppLabel() { + return mAppLabel; + } + + public void setAppLabel(String appLabel) { + mAppLabel = appLabel; + } + + public Drawable getAppIcon() { + return mAppIcon; + } + + public void setAppIcon(Drawable appIcon) { + mAppIcon = appIcon; + } + + public int getAppActivity() { + return mAppActivity; + } + + public void setAppActivity(int activity) { + mAppActivity = activity; + } + + public String getPkgName() { + return mPkgName; + } + + public void setPkgName(String pkgName) { + mPkgName = pkgName; + } + + public int getUid() { + return mUid; + } + + public void setUid(int uid) { + mUid = uid; + } + + public int getWakeupTimes() { + return mWakeupTimes; + } + + public void setWakeupTimes(int wakeupTimes) { + mWakeupTimes = wakeupTimes; + } + + public int getWowWakeupTimes() { + return mWowWakeupTimes; + } + + public void setWowWakeupTimes(int wowWakeupTimes) { + mWowWakeupTimes = wowWakeupTimes; + } + + public long getTotalWakeupStatsTime() { + return mTotalWakeupStatsTime; + } + + public void setTotalWakeupStatsTime(long totalWakeupStatsTime) { + mTotalWakeupStatsTime = totalWakeupStatsTime; + } + + public long getWakeupStatsTime() { + return mWakeupStatsTime; + } + + public void setWakeupStatsTime(long wakeupStatsTime) { + mWakeupStatsTime = wakeupStatsTime; + } + + public int getAppList() { + return mAppList; + } + + public void setAppList(int appList) { + mAppList = appList; + } + + public float getPowerSaver() { + return mPowerSaver; + } + + public void setPowerSaver(float powerSaver) { + mPowerSaver = powerSaver; + } + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mUid); + dest.writeInt(mAppList); + dest.writeInt(mWakeupTimes); + dest.writeInt(mWowWakeupTimes); + dest.writeString(mPkgName); + dest.writeLong(mWakeupStatsTime); + dest.writeLong(mTotalWakeupStatsTime); + } +} diff --git a/core/java/com/nvidia/framework/NvConstants.java b/core/java/com/nvidia/framework/NvConstants.java new file mode 100644 index 0000000000000..12933c623a54b --- /dev/null +++ b/core/java/com/nvidia/framework/NvConstants.java @@ -0,0 +1,66 @@ +package com.nvidia.framework; + +import java.util.Arrays; +import java.util.List; + +public class NvConstants { + public static final String ACTION_DUMP_SERVICE_REPORT = "com.android.ams.action.DUMP_REPORT"; + public static final String ACTION_TV_LAUNCHER_LAUNCH = "com.android.ams.action.TV_LAUNCHER_LAUNCH"; + public static final String ALEXA_ACTION_ALEXA_OOBE_CANCELLED = "com.nvidia.shield.alexaexternal.action.ALEXA_OOBE_CANCELLED "; + public static final String ALEXA_ACTION_ASSISTANT_APP_RESUMED = "com.nvidia.shield.alexaexternal.action.ASSISTANT_APP_RESUMED"; + public static final String ALEXA_ACTION_BOOT_COMPLETED = "com.nvidia.shield.alexaexternal.action.BOOT_COMPLETED"; + public static final String ALEXA_ACTION_CHANGE_MENU_BUTTON_ALEXA_SETTING = "com.nvidia.shield.alexaexternal.action.CHANGE_MENU_BUTTON_ALEXA_SETTING"; + public static final String ALEXA_ACTION_START_ALEXA_OOBE = "com.nvidia.shield.alexaexternal.action.START_ALEXA_OOBE"; + public static final String ALEXA_ACTION_START_EXTERNAL_SERVICE = "com.nvidia.shield.alexaexternal.action.START_EXTERNAL_SERVICE"; + public static final String ALEXA_ACTION_START_UTTERANCE = "com.nvidia.shield.alexaexternal.action.START_UTTERANCE"; + public static final String ALEXA_ASSISTANT_APP_ACTIVITY_NAME = "com.amazon.alexa.multimodal.LaunchActivity"; + public static final String ALEXA_ASSISTANT_APP_PACKAGE_NAME = "com.amazon.alexa.multimodal.nyla"; + public static final String ALEXA_EXTERNAL_APP_ACTIVITY_NAME = "com.nvidia.shield.alexaexternal.MainActivity"; + public static final String ALEXA_EXTERNAL_APP_PACKAGE_NAME = "com.nvidia.shield.alexaexternal"; + public static final String ALEXA_EXTERNAL_APP_SERVICE_NAME = "com.nvidia.shield.alexaexternal.AlexaExternalService"; + public static final int ALEXA_OOBE_STATE_CANCELLED = 2; + public static final int ALEXA_OOBE_STATE_COMPLETE = 3; + public static final int ALEXA_OOBE_STATE_HAS_NOT_STARTED = 0; + public static final int ALEXA_OOBE_STATE_IN_PROGRESS = 1; + public static final String EXTRA_MESSENGER_CB = "messenger_cb"; + public static final String EXTRA_REPORT_DATA = "service_report"; + public static final String INTENT_EXTRA_ALEXA_LAUNCH_METHOD = "LAUNCH_METHOD"; + public static final String INTENT_EXTRA_AUDIO_BUFFERING_STATE = "AUDIO_BUFFERING_STATE"; + public static final String METHOD_DEVICE_SETUP = "DEVICE_SETUP"; + public static final String METHOD_HOME_SCREEN = "HOME_SCREEN"; + public static final String METHOD_OTA_UPDATE = "OTA_UPDATE"; + public static final String METHOD_REMOTE_MENU = "REMOTE_MENU"; + public static final int NV_AUTOMATIC_POWER_MODE_DISABLED = -1; + public static final String NV_BENCHMARK_APP_LAUNCH_INTENT = "com.nvidia.benchmarkblocker.APP_LAUNCH"; + public static final String NV_BENCHMARK_BLOCKER_CLASS_NAME = "com.nvidia.benchmarkblocker.NvBenchmarkBlockerService"; + public static final String NV_BENCHMARK_BLOCKER_PACKAGE_NAME = "com.nvidia.benchmarkblocker"; + public static final List NV_DEFAULT_GAME_MODE_ENABLE_APPS = Arrays.asList("com.google.stadia.androidtv", "com.google.android.apps.tachyon", "com.gamepass"); + public static final int NV_MAX_BATTERY_LEVEL = 100; + public static final int NV_POWER_MODE_BATTERY_SAVER = 2; + public static final int NV_POWER_MODE_CUSTOM = 3; + public static final int NV_POWER_MODE_INVALID = -1; + public static final int NV_POWER_MODE_MAX_PERF = 0; + public static final int NV_POWER_MODE_OPTIMIZED = 1; + public static final String NvAppClose = "com.nvidia.app.close"; + public static final String NvBatteryMonitor = "com.nvidia.NvCPLSvc.BatteryMonitor"; + public static final String NvCPLService = "com.nvidia.NvCPLSvc.NvCPLService"; + public static final String NvCPLSvc = "com.nvidia.NvCPLSvc"; + public static final String NvMaxGpuMode = "com.nvidia.NvCPLSvc.MaxGpuMode"; + public static final String NvOrigin = "com.nvidia.NvCPLSvc.Origin"; + public static final String NvPowerMode = "com.nvidia.NvCPLSvc.NV_POWER_MODE"; + public static final String NvPowerModeProperty = "persist.vendor.sys.NV_POWER_MODE"; + public static final String NvStateId = "com.nvidia.NvCPLSvc.StateID"; + public static final String NvTCPDivisor = "com.nvidia.NvCPLSvc.NV_TCPDIVISOR"; + public static final String NvThermalStats = "com.nvidia.peripheralservice.NvThermalStats"; + public static final String PACKAGE_NAME_APPSELECTOR = "com.nvidia.shield.appselector"; + public static final String PACKAGE_NAME_TV_LAUNCHER = "com.google.android.tvlauncher"; + public static final int POWER_CUSTOM = 3; + public static final int POWER_MAX_PERF = 0; + public static final int POWER_OPTIMIZED = 1; + public static final int POWER_SAVE_BATTERY = 2; + public static final String PROPERTY_DISABLE_BUFFER_AGE = "debug.hwui.disable_buffer_age"; + public static final int WIFI_FREQUENCY_BAND_2GHZ = 2; + public static final int WIFI_FREQUENCY_BAND_5GHZ = 1; + public static final int WIFI_FREQUENCY_BAND_AUTO = 0; + public static final int maxPerfMode = 0; +} diff --git a/core/java/com/nvidia/profilemanager/NvAppProfileSettingId.java b/core/java/com/nvidia/profilemanager/NvAppProfileSettingId.java new file mode 100644 index 0000000000000..ca547a08e300a --- /dev/null +++ b/core/java/com/nvidia/profilemanager/NvAppProfileSettingId.java @@ -0,0 +1,95 @@ +package com.nvidia.profilemanager; + +public class NvAppProfileSettingId { + public static int RESOVERRIDE_SCALE_FACTOR = 5; + public static int STEREO_PERF_WIDTH = 7; + public static int RESOVERRIDE_PERF_WIDTH = 8; + public static int FORCE_HW_UI = 9; + public static int CORE_BIAS = 17; + public static int CPU_FREQ_BIAS = 18; + public static int GPU_CORE_CAP = 19; + public static int SCALING_MIN_FREQ = 20; + public static int GPU_SCALING = 21; + public static int PBC_PWR_LIMIT = 22; + public static int FAN_PWM_CAP = 23; + public static int VOLT_TEMP_MODE = 24; + public static int FRAME_RATE_LIMIT = 26; + public static int DISABLE_APM = 29; + public static int EDP_MODE = 30; + public static int GPU_MODE = 35; + public static int STYLUS_FINGER_ONLY_MODE = 40; + public static int STEREO_PERF_SCALE_FACTOR = 43; + public static int DISABLE_BUFFER_AGE = 47; + public static int SYSTEM_POWER_MODE = 48; + public static int CUSTOM_PROFILE_BLACKLIST = 51; + public static int BLOCK_NETWORK_ACCESS = 53; + public static int MAX_CPU_CORES = 58; + public static int AGGRESSIVE_PRISM_ENABLE = 59; + public static int BLOCK_ON_NETWORK = 60; + public static int MAX_CPU_FREQ_PCT = 61; + public static int DEPTH_COMPRESSION = 62; + public static int OGL_THREADCONTROL = 63; + public static int OGL_SKIP_ENABLED_UNSET_ARRAY = 64; + public static int CMU_GTM_MAPPING_LUT = 65; + public static int OGL_CLEAR_METERING = 66; + public static int KILL_PROCESS_BELOW_ADJ = 67; + public static int EGL_REPORT_ES1_CONFIG_ONLY = 69; + public static int VIDEO_IQ = 70; + public static int CAMERA_MEMORY = 71; + public static int PERF_FP = 72; + public static int MIN_CPU_CORES = 73; + public static int PROMOTE_SURFACE_TO_32BIT = 74; + public static int AFFINITY_DAEMON_ENABLE = 75; + public static int CUSTOM_PROFILE_SYSTEM_WHITELIST = 76; + public static int FORCEONCPU = 77; + public static int KEY_DEVICE_ID_HASH = 78; + public static int GSYNC_WHITELIST = 79; + public static int GSYNC_BLACKLIST = 80; + public static int TOUCH_MODE = 81; + public static int DISPLAY_UPSCALE_HEIGHT = 82; + public static int DISPLAY_UPSCALE_WIDTH = 83; + public static int OGL_APP_RUNTIME_VERTEX_ATTRIB_SOURCING = 84; + public static int MAXWELL_TILEDCACHE = 85; + public static int OGL_HIDE_EXTENSIONS_STRING = 86; + public static int NVIDIA_WEBVIEW = 87; + public static int OGL_HIDE_EXTENSIONS = 88; + public static int HDD_DIALOG_FREQ = 89; + public static int HDD_DIALOG_TEXT = 90; + public static int HDD_DIALOG_THRESHOLD = 91; + public static int HDD_DIALOG_ENABLE = 92; + public static int TCP_DIVISOR = 93; + public static int BBC_APPS = 94; + public static int HDD_STATS_ENABLE = 95; + public static int HDD_DIALOG_REMIND_ME_LATER_FREQ = 96; + public static int HDD_STATS_FREQ = 97; + public static int HDD_DIALOG_STR_THRESHOLD = 98; + public static int OGL_VERSION_OVERRIDE = 99; + public static int OGL_ES_VERSION_OVERRIDE = 100; + public static int MULTI_CHANNEL_SWITCH_MODE = 101; + public static int VIDEO_FRC_ENABLE = 102; + public static int VIDEO_SECURE_DECODE = 103; + public static int VIDEO_TS_FILTERING = 104; + public static int NVIDIA_VIDEO_CERTIFICATION_ENABLED = 105; + public static int OGL_SHADER_PORTABILITY_WARNINGS = 106; + public static int OGL_THREADCONTROL2 = 107; + public static int OGL_EXTRA_CGC_OPTION = 108; + public static int DISABLE_APP = 109; + public static int SET_REGION_LIST = 110; + public static int PINNING_ENABLE = 111; + public static int PINNING_ORDER = 112; + public static int GPU_MODESET_ENABLE = 113; + public static int AVS_DELAY_ENABLE = 114; + public static int BLACKLIST_USB_30 = 115; + public static int DRM_DIALOG_ENABLE = 116; + public static int FILTER_EGLCONFIGS = 117; + public static int SHIELD_LOGGING = 118; + public static int APP_AUDIO_SWITCH_TO_STEREO = 119; + public static int WHITELIST_CUSTOMIZE_BANNER = 120; + public static int NV_MAPPER_GAME_LIST = 121; + public static int NV_MULTI_CAPTURE_MOD = 122; + public static int MEDIA_ENABLE_MSD_HAL = 123; + public static int DEEPISP_DISABLED = 124; + public static int OGL_GPFIFO_SIZE_ENTRIES = 125; + public static int FORCE_QUERY_PACKAGES = 126; + public static int AUTO_MEDIA_SCAN_PACKAGES = 127; +} diff --git a/core/java/com/oplus/theme/OplusThemeUtil.java b/core/java/com/oplus/theme/OplusThemeUtil.java new file mode 100644 index 0000000000000..cea1cfd31b834 --- /dev/null +++ b/core/java/com/oplus/theme/OplusThemeUtil.java @@ -0,0 +1,9 @@ +package com.oplus.theme; + +import android.compat.annotation.UnsupportedAppUsage; + +public class OplusThemeUtil { + /** @hide */ + @UnsupportedAppUsage + public static String CUSTOM_THEME_PATH = "/data/theme/com.oplus.camera"; +} diff --git a/core/java/com/oplus/util/OplusTypeCastingHelper.java b/core/java/com/oplus/util/OplusTypeCastingHelper.java new file mode 100644 index 0000000000000..4819e9f20b720 --- /dev/null +++ b/core/java/com/oplus/util/OplusTypeCastingHelper.java @@ -0,0 +1,10 @@ +package com.oplus.util; + +public final class OplusTypeCastingHelper { + public static T typeCasting(Class type, Object object) { + if (object != null && type.isInstance(object)) { + return type.cast(object); + } + return null; + } +} diff --git a/core/jni/android_hardware_Camera.cpp b/core/jni/android_hardware_Camera.cpp index 2ae2e1865c033..5412f118703e6 100644 --- a/core/jni/android_hardware_Camera.cpp +++ b/core/jni/android_hardware_Camera.cpp @@ -68,6 +68,18 @@ struct fields_t { jmethodID rect_constructor; jmethodID face_constructor; jmethodID point_constructor; + jfieldID face_sm_degree; + jfieldID face_sm_score; + jfieldID face_blink_detected; + jfieldID face_gaze_angle; + jfieldID face_updown_dir; + jfieldID face_leftright_dir; + jfieldID face_roll_dir; + jfieldID face_leye_blink; + jfieldID face_reye_blink; + jfieldID face_left_right_gaze; + jfieldID face_top_bottom_gaze; + jfieldID face_recognised; }; static fields_t fields; @@ -106,6 +118,7 @@ class JNICameraContext: public CameraListener jclass mFaceClass; // strong reference to Face class jclass mRectClass; // strong reference to Rect class jclass mPointClass; // strong reference to Point class + bool mIsExtendedFace; Mutex mLock; /* @@ -157,8 +170,16 @@ JNICameraContext::JNICameraContext(JNIEnv* env, jobject weak_this, jclass clazz, mCameraJClass = (jclass)env->NewGlobalRef(clazz); mCamera = camera; - jclass faceClazz = env->FindClass("android/hardware/Camera$Face"); - mFaceClass = (jclass) env->NewGlobalRef(faceClazz); + jclass extendedfaceClazz = env->FindClass("com/qualcomm/qti/camera/ExtendedFace"); + if (NULL != extendedfaceClazz) { + mFaceClass = (jclass) env->NewGlobalRef(extendedfaceClazz); + mIsExtendedFace = true; + } else { + env->ExceptionClear(); + jclass faceClazz = env->FindClass("android/hardware/Camera$Face"); + mFaceClass = (jclass) env->NewGlobalRef(faceClazz); + mIsExtendedFace = false; + } jclass rectClazz = env->FindClass("android/graphics/Rect"); mRectClass = (jclass) env->NewGlobalRef(rectClazz); @@ -410,7 +431,6 @@ void JNICameraContext::postMetadata(JNIEnv *env, int32_t msgType, camera_frame_m env->SetIntField(rect, fields.rect_top, metadata->faces[i].rect[1]); env->SetIntField(rect, fields.rect_right, metadata->faces[i].rect[2]); env->SetIntField(rect, fields.rect_bottom, metadata->faces[i].rect[3]); - env->SetObjectField(face, fields.face_rect, rect); env->SetIntField(face, fields.face_score, metadata->faces[i].score); @@ -439,6 +459,21 @@ void JNICameraContext::postMetadata(JNIEnv *env, int32_t msgType, camera_frame_m env->SetIntField(mouth, fields.point_y, metadata->faces[i].mouth[1]); env->SetObjectField(face, fields.face_mouth, mouth); env->DeleteLocalRef(mouth); + + if (mIsExtendedFace) { + env->SetIntField(face, fields.face_sm_degree, metadata->faces[i].smile_degree); + env->SetIntField(face, fields.face_sm_score, metadata->faces[i].smile_score); + env->SetIntField(face, fields.face_blink_detected, metadata->faces[i].blink_detected); + env->SetIntField(face, fields.face_recognised, metadata->faces[i].face_recognised); + env->SetIntField(face, fields.face_gaze_angle, metadata->faces[i].gaze_angle); + env->SetIntField(face, fields.face_updown_dir, metadata->faces[i].updown_dir); + env->SetIntField(face, fields.face_leftright_dir, metadata->faces[i].leftright_dir); + env->SetIntField(face, fields.face_roll_dir, metadata->faces[i].roll_dir); + env->SetIntField(face, fields.face_leye_blink, metadata->faces[i].leye_blink); + env->SetIntField(face, fields.face_reye_blink, metadata->faces[i].reye_blink); + env->SetIntField(face, fields.face_left_right_gaze, metadata->faces[i].left_right_gaze); + env->SetIntField(face, fields.face_top_bottom_gaze, metadata->faces[i].top_bottom_gaze); + } } env->DeleteLocalRef(face); @@ -476,6 +511,56 @@ void JNICameraContext::setCallbackMode(JNIEnv *env, bool installed, bool manualM } } +static void android_hardware_Camera_setLongshot(JNIEnv *env, jobject thiz, jboolean enable) +{ + ALOGV("setLongshot"); + JNICameraContext* context; + status_t rc; + sp camera = get_native_camera(env, thiz, &context); + if (camera == 0) return; + + if ( enable ) { + rc = camera->sendCommand(CAMERA_CMD_LONGSHOT_ON, 0, 0); + } else { + rc = camera->sendCommand(CAMERA_CMD_LONGSHOT_OFF, 0, 0); + } + + if (rc != NO_ERROR) { + jniThrowException(env, "java/lang/RuntimeException", "enabling longshot mode failed"); + } +} + +static void android_hardware_Camera_sendHistogramData(JNIEnv *env, jobject thiz) + { + ALOGV("sendHistogramData" ); + JNICameraContext* context; + status_t rc; + sp camera = get_native_camera(env, thiz, &context); + if (camera == 0) return; + + rc = camera->sendCommand(CAMERA_CMD_HISTOGRAM_SEND_DATA, 0, 0); + + if (rc != NO_ERROR) { + jniThrowException(env, "java/lang/RuntimeException", "send histogram data failed"); + } + } + static void android_hardware_Camera_setHistogramMode(JNIEnv *env, jobject thiz, jboolean mode) + { + ALOGV("setHistogramMode: mode:%d", (int)mode); + JNICameraContext* context; + status_t rc; + sp camera = get_native_camera(env, thiz, &context); + if (camera == 0) return; + + if(mode == true) + rc = camera->sendCommand(CAMERA_CMD_HISTOGRAM_ON, 0, 0); + else + rc = camera->sendCommand(CAMERA_CMD_HISTOGRAM_OFF, 0, 0); + + if (rc != NO_ERROR) { + jniThrowException(env, "java/lang/RuntimeException", "set histogram mode failed"); + } + } void JNICameraContext::addCallbackBuffer( JNIEnv *env, jbyteArray cbb, int msgType) { @@ -851,7 +936,25 @@ static void android_hardware_Camera_setHasPreviewCallback(JNIEnv *env, jobject t context->setCallbackMode(env, installed, manualBuffer); } -static void android_hardware_Camera_addCallbackBuffer(JNIEnv *env, jobject thiz, jbyteArray bytes, jint msgType) { +static void android_hardware_Camera_setMetadataCb(JNIEnv *env, jobject thiz, jboolean mode) +{ + ALOGV("setMetadataCb: mode:%d", (int)mode); + JNICameraContext* context; + status_t rc; + sp camera = get_native_camera(env, thiz, &context); + if (camera == 0) return; + + if(mode == true) + rc = camera->sendCommand(CAMERA_CMD_METADATA_ON, 0, 0); + else + rc = camera->sendCommand(CAMERA_CMD_METADATA_OFF, 0, 0); + + if (rc != NO_ERROR) { + jniThrowException(env, "java/lang/RuntimeException", "set metadata mode failed"); + } +} + +static void android_hardware_Camera_addCallbackBuffer(JNIEnv *env, jobject thiz, jbyteArray bytes, int msgType) { ALOGV("addCallbackBuffer: 0x%x", msgType); JNICameraContext* context = reinterpret_cast(env->GetLongField(thiz, fields.context)); @@ -1140,6 +1243,10 @@ static const JNINativeMethod camMethods[] = { {"native_autoFocus", "()V", (void *)android_hardware_Camera_autoFocus}, {"native_cancelAutoFocus", "()V", (void *)android_hardware_Camera_cancelAutoFocus}, {"native_takePicture", "(I)V", (void *)android_hardware_Camera_takePicture}, + { "native_setHistogramMode", "(Z)V", (void *)android_hardware_Camera_setHistogramMode }, + { "native_setMetadataCb", "(Z)V", (void *)android_hardware_Camera_setMetadataCb }, + { "native_sendHistogramData", "()V", (void *)android_hardware_Camera_sendHistogramData }, + { "native_setLongshot", "(Z)V", (void *)android_hardware_Camera_setLongshot }, {"native_setParameters", "(Ljava/lang/String;)V", (void *)android_hardware_Camera_setParameters}, {"native_getParameters", "()Ljava/lang/String;", @@ -1199,6 +1306,27 @@ int register_android_hardware_Camera(JNIEnv *env) { "android/graphics/Point", "y", "I", &fields.point_y}, }; + field extendedfacefields_to_find[] = { + { "com/qualcomm/qti/camera/ExtendedFace", "rect", "Landroid/graphics/Rect;", &fields.face_rect }, + { "com/qualcomm/qti/camera/ExtendedFace", "score", "I", &fields.face_score }, + { "com/qualcomm/qti/camera/ExtendedFace", "id", "I", &fields.face_id }, + { "com/qualcomm/qti/camera/ExtendedFace", "leftEye", "Landroid/graphics/Point;", &fields.face_left_eye }, + { "com/qualcomm/qti/camera/ExtendedFace", "rightEye", "Landroid/graphics/Point;", &fields.face_right_eye }, + { "com/qualcomm/qti/camera/ExtendedFace", "mouth", "Landroid/graphics/Point;", &fields.face_mouth }, + { "com/qualcomm/qti/camera/ExtendedFace", "smileDegree", "I", &fields.face_sm_degree }, + { "com/qualcomm/qti/camera/ExtendedFace", "smileScore", "I", &fields.face_sm_score }, + { "com/qualcomm/qti/camera/ExtendedFace", "blinkDetected", "I", &fields.face_blink_detected }, + { "com/qualcomm/qti/camera/ExtendedFace", "faceRecognized", "I", &fields.face_recognised }, + { "com/qualcomm/qti/camera/ExtendedFace", "gazeAngle", "I", &fields.face_gaze_angle }, + { "com/qualcomm/qti/camera/ExtendedFace", "updownDir", "I", &fields.face_updown_dir }, + { "com/qualcomm/qti/camera/ExtendedFace", "leftrightDir", "I", &fields.face_leftright_dir }, + { "com/qualcomm/qti/camera/ExtendedFace", "rollDir", "I", &fields.face_roll_dir }, + { "com/qualcomm/qti/camera/ExtendedFace", "leyeBlink", "I", &fields.face_leye_blink }, + { "com/qualcomm/qti/camera/ExtendedFace", "reyeBlink", "I", &fields.face_reye_blink }, + { "com/qualcomm/qti/camera/ExtendedFace", "leftrightGaze", "I", &fields.face_left_right_gaze }, + { "com/qualcomm/qti/camera/ExtendedFace", "topbottomGaze", "I", &fields.face_top_bottom_gaze }, + }; + find_fields(env, fields_to_find, NELEM(fields_to_find)); jclass clazz = FindClassOrDie(env, "android/hardware/Camera"); @@ -1218,6 +1346,14 @@ int register_android_hardware_Camera(JNIEnv *env) return -1; } + clazz = env->FindClass("com/qualcomm/qti/camera/ExtendedFace"); + if (NULL != clazz) { + fields.face_constructor = env->GetMethodID(clazz, "", "()V"); + find_fields(env, extendedfacefields_to_find, NELEM(extendedfacefields_to_find)); + } else { + env->ExceptionClear(); + } + // Register native functions return RegisterMethodsOrDie(env, "android/hardware/Camera", camMethods, NELEM(camMethods)); } diff --git a/core/jni/android_hardware_camera2_DngCreator.cpp b/core/jni/android_hardware_camera2_DngCreator.cpp index 828f2eb76c60b..f7d4e4cc2b8c0 100644 --- a/core/jni/android_hardware_camera2_DngCreator.cpp +++ b/core/jni/android_hardware_camera2_DngCreator.cpp @@ -1514,6 +1514,19 @@ static sp DngCreator_setup(JNIEnv* env, jobject thiz, uint32_t image writer); } + // MIUI ADD START + { + // market name + // Use "" to represent unknown productname as suggested in XiaoMi spec. + std::string productname = GetProperty("ro.product.marketname", ""); + uint32_t count = static_cast(productname.size()) + 1; + + BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_XIAOMI_PRODUCT, count, + reinterpret_cast(productname.c_str()), TIFF_IFD_0), env, TAG_XIAOMI_PRODUCT, + writer); + } + // END + { // x resolution uint32_t xres[] = { 72, 1 }; // default 72 ppi @@ -1660,6 +1673,38 @@ static sp DngCreator_setup(JNIEnv* env, jobject thiz, uint32_t image TIFF_IFD_0), env, TAG_FOCALLENGTH, writer); } + { + // FocalLengthIn35mmFilm + uint16_t focalLengthIn35mmFilm = 0; + uint32_t tag = 0; + sp vTags; + sp cache = VendorTagDescriptorCache::getGlobalVendorTagCache(); + if (cache) { + auto vendorId = results.getVendorId(); + cache->getVendorTagDescriptor(vendorId, &vTags); + } + + if (vTags != NULL) { + const char *section = "com.xiaomi.sensor.info"; + const char *TagName = "focalLength35mm"; + const String8 sectionName(section); + const String8 tagName(TagName); + status_t ret = vTags->lookupTag(tagName, sectionName, &tag); + if (ret == 0) { + camera_metadata_entry entry = results.find(tag); + if (entry.count != 0) { + focalLengthIn35mmFilm =static_cast (entry.data.f[0] + 0.5f); + BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_FOCALLLENGTHIN35MMFILM, 1, &focalLengthIn35mmFilm, + TIFF_IFD_0), env, TAG_FOCALLLENGTHIN35MMFILM, writer); + } else { + ALOGW("%s: get focalLength35mm failed.", __FUNCTION__); + } + } + } else { + ALOGW("%s:com.xiaomi.sensor.info.focalLength35mm vTags is null.", __FUNCTION__); + } + } + { // f number camera_metadata_entry entry = diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp index 01c18e4bb4771..ddb65651d6076 100644 --- a/core/jni/android_media_AudioSystem.cpp +++ b/core/jni/android_media_AudioSystem.cpp @@ -1029,6 +1029,7 @@ static void convertAudioGainConfigToNative(JNIEnv *env, int *nValues = env->GetIntArrayElements(jValues, NULL); size_t size = env->GetArrayLength(jValues); memcpy(nAudioGainConfig->values, nValues, size * sizeof(int)); + env->ReleaseIntArrayElements(jValues, nValues, JNI_ABORT); env->DeleteLocalRef(jValues); } diff --git a/core/jni/fd_utils.cpp b/core/jni/fd_utils.cpp index 0eb7c4aee2876..22c333591eb4e 100644 --- a/core/jni/fd_utils.cpp +++ b/core/jni/fd_utils.cpp @@ -44,6 +44,7 @@ static const char* kPathAllowlist[] = { "/sys/kernel/debug/tracing/trace_marker", "/sys/kernel/tracing/trace_marker", "/system/framework/framework-res.apk", + "/system/framework/org.lineageos.platform-res.apk", "/dev/urandom", "/dev/ion", "/dev/dri/renderD129", // Fixes b/31172436 @@ -122,6 +123,7 @@ bool FileDescriptorAllowlist::IsAllowed(const std::string& path) const { static const char* kSystemSystemExtOverlayDir = "/system/system_ext/overlay/"; static const char* kSystemExtOverlayDir = "/system_ext/overlay"; static const char* kSystemOdmOverlayDir = "/system/odm/overlay"; + static const char* kVendorOdmOverlayDir = "/vendor/odm/overlay"; static const char* kOdmOverlayDir = "/odm/overlay"; static const char* kSystemOemOverlayDir = "/system/oem/overlay"; static const char* kOemOverlayDir = "/oem/overlay"; @@ -135,6 +137,7 @@ bool FileDescriptorAllowlist::IsAllowed(const std::string& path) const { android::base::StartsWith(path, kSystemSystemExtOverlayDir) || android::base::StartsWith(path, kSystemExtOverlayDir) || android::base::StartsWith(path, kSystemOdmOverlayDir) || + android::base::StartsWith(path, kVendorOdmOverlayDir) || android::base::StartsWith(path, kOdmOverlayDir) || android::base::StartsWith(path, kSystemOemOverlayDir) || android::base::StartsWith(path, kOemOverlayDir)) && diff --git a/core/proto/android/server/appstatetracker.proto b/core/proto/android/server/appstatetracker.proto index f5583d4f476f7..0d0fb097d963b 100644 --- a/core/proto/android/server/appstatetracker.proto +++ b/core/proto/android/server/appstatetracker.proto @@ -25,7 +25,7 @@ option java_multiple_files = true; // Dump from com.android.server.AppStateTracker. // -// Next ID: 14 +// Next ID: 15 message AppStateTrackerProto { option (.android.msg_privacy).dest = DEST_AUTOMATIC; @@ -41,6 +41,9 @@ message AppStateTrackerProto { // UIDs currently in the foreground. repeated int32 foreground_uids = 11; + // App ids that are in power-save system exemption list. + repeated int32 power_save_system_exempt_app_ids = 14; + // App ids that are in power-save exemption list. repeated int32 power_save_exempt_app_ids = 3; diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 23df4e3f41dd0..7b6234d7f4896 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -831,6 +831,7 @@ + @@ -859,6 +860,22 @@ + + + + + + + + + + + + + + + + @@ -940,6 +957,10 @@ android:knownCerts="@array/config_setContactsDefaultAccountKnownSigners" android:featureFlag="android.provider.new_default_account_api_enabled"/> + + + @@ -9194,39 +9215,10 @@ android:protectionLevel="internal|role" android:featureFlag="com.android.window.flags.enable_window_repositioning_api" /> - - - - - - - - + + @@ -9261,7 +9253,7 @@ android:killAfterRestore="false" android:icon="@drawable/ic_launcher_android" android:supportsRtl="true" - android:theme="@style/Theme.DeviceDefault.Light.DarkActionBar" + android:theme="@style/Theme.DeviceDefault.System" android:defaultToDeviceProtectedStorage="true" android:forceQueryable="true" android:directBootAware="true"> diff --git a/core/res/res/anim/activity_close_exit.xml b/core/res/res/anim/activity_close_exit.xml index a6710490d820d..10ac35fb21730 100644 --- a/core/res/res/anim/activity_close_exit.xml +++ b/core/res/res/anim/activity_close_exit.xml @@ -18,6 +18,7 @@ --> - diff --git a/core/res/res/color-night/shade_panel_fg_color.xml b/core/res/res/color-night/shade_panel_fg_color.xml index 1bde31999e35d..ac9be20c98885 100644 --- a/core/res/res/color-night/shade_panel_fg_color.xml +++ b/core/res/res/color-night/shade_panel_fg_color.xml @@ -15,6 +15,6 @@ --> - diff --git a/core/res/res/color/config_progress_background_tint.xml b/core/res/res/color/config_progress_background_tint.xml index b086e20bf1619..dfc914e76c8d0 100644 --- a/core/res/res/color/config_progress_background_tint.xml +++ b/core/res/res/color/config_progress_background_tint.xml @@ -15,5 +15,5 @@ --> - + diff --git a/core/res/res/color/shade_panel_bg_color.xml b/core/res/res/color/shade_panel_bg_color.xml index b5678f982c7fd..899176d171818 100644 --- a/core/res/res/color/shade_panel_bg_color.xml +++ b/core/res/res/color/shade_panel_bg_color.xml @@ -15,6 +15,6 @@ --> - diff --git a/core/res/res/color/shade_panel_fg_color.xml b/core/res/res/color/shade_panel_fg_color.xml index 7080008a0821a..7e8f2a6893a3d 100644 --- a/core/res/res/color/shade_panel_fg_color.xml +++ b/core/res/res/color/shade_panel_fg_color.xml @@ -15,6 +15,6 @@ --> - diff --git a/core/res/res/drawable-hdpi/stat_sys_data_usb.png b/core/res/res/drawable-hdpi/stat_sys_data_usb.png deleted file mode 100644 index 457d5fc186c66..0000000000000 Binary files a/core/res/res/drawable-hdpi/stat_sys_data_usb.png and /dev/null differ diff --git a/core/res/res/drawable-ldpi/stat_sys_data_usb.png b/core/res/res/drawable-ldpi/stat_sys_data_usb.png deleted file mode 100644 index dbf53212f341b..0000000000000 Binary files a/core/res/res/drawable-ldpi/stat_sys_data_usb.png and /dev/null differ diff --git a/core/res/res/drawable-mdpi/stat_sys_data_usb.png b/core/res/res/drawable-mdpi/stat_sys_data_usb.png deleted file mode 100644 index 9294e5fa4015e..0000000000000 Binary files a/core/res/res/drawable-mdpi/stat_sys_data_usb.png and /dev/null differ diff --git a/core/res/res/drawable-xhdpi/stat_sys_data_usb.png b/core/res/res/drawable-xhdpi/stat_sys_data_usb.png deleted file mode 100644 index c5fd0134a5b9a..0000000000000 Binary files a/core/res/res/drawable-xhdpi/stat_sys_data_usb.png and /dev/null differ diff --git a/core/res/res/drawable-xxhdpi/stat_sys_data_usb.png b/core/res/res/drawable-xxhdpi/stat_sys_data_usb.png deleted file mode 100644 index 501b89f4d5d3c..0000000000000 Binary files a/core/res/res/drawable-xxhdpi/stat_sys_data_usb.png and /dev/null differ diff --git a/core/res/res/drawable/ic_link.xml b/core/res/res/drawable/ic_link.xml new file mode 100644 index 0000000000000..97322a4f24bdf --- /dev/null +++ b/core/res/res/drawable/ic_link.xml @@ -0,0 +1,9 @@ + + + diff --git a/core/res/res/drawable/pocket_mode_illustration.xml b/core/res/res/drawable/pocket_mode_illustration.xml new file mode 100644 index 0000000000000..3b55a10cbaf60 --- /dev/null +++ b/core/res/res/drawable/pocket_mode_illustration.xml @@ -0,0 +1,19 @@ + + + + + + diff --git a/core/res/res/drawable/popup_background_material.xml b/core/res/res/drawable/popup_background_material.xml index 9ad7bfc68122b..5910b9f163a51 100644 --- a/core/res/res/drawable/popup_background_material.xml +++ b/core/res/res/drawable/popup_background_material.xml @@ -18,7 +18,7 @@ android:shape="rectangle"> + android:radius="?attr/dialogCornerRadius" /> diff --git a/core/res/res/drawable/progress_large.xml b/core/res/res/drawable/progress_large.xml index 4f016bcc2e83e..44b410393182f 100644 --- a/core/res/res/drawable/progress_large.xml +++ b/core/res/res/drawable/progress_large.xml @@ -21,5 +21,5 @@ android:drawable="@drawable/spinner_black_76" android:pivotX="50%" android:pivotY="50%" - android:framesCount="12" - android:frameDuration="100" /> + android:framesCount="48" + android:frameDuration="25" /> diff --git a/core/res/res/drawable/progress_large_white.xml b/core/res/res/drawable/progress_large_white.xml index c690ed4e0e9ae..6c2388c680a3f 100644 --- a/core/res/res/drawable/progress_large_white.xml +++ b/core/res/res/drawable/progress_large_white.xml @@ -21,5 +21,5 @@ android:drawable="@drawable/spinner_white_76" android:pivotX="50%" android:pivotY="50%" - android:framesCount="12" - android:frameDuration="100" /> + android:framesCount="48" + android:frameDuration="25" /> diff --git a/core/res/res/drawable/progress_medium.xml b/core/res/res/drawable/progress_medium.xml index eb1bd50d17d73..82ad686b67390 100644 --- a/core/res/res/drawable/progress_medium.xml +++ b/core/res/res/drawable/progress_medium.xml @@ -21,5 +21,5 @@ android:drawable="@drawable/spinner_black_48" android:pivotX="50%" android:pivotY="50%" - android:framesCount="12" - android:frameDuration="100" /> + android:framesCount="48" + android:frameDuration="25" /> diff --git a/core/res/res/drawable/progress_medium_white.xml b/core/res/res/drawable/progress_medium_white.xml index b4f9b318a902a..886ee05bc9c78 100644 --- a/core/res/res/drawable/progress_medium_white.xml +++ b/core/res/res/drawable/progress_medium_white.xml @@ -21,5 +21,5 @@ android:drawable="@drawable/spinner_white_48" android:pivotX="50%" android:pivotY="50%" - android:framesCount="12" - android:frameDuration="100" /> + android:framesCount="48" + android:frameDuration="25" /> diff --git a/core/res/res/drawable/progress_small.xml b/core/res/res/drawable/progress_small.xml index e0ee5e47d8305..1881da3848924 100644 --- a/core/res/res/drawable/progress_small.xml +++ b/core/res/res/drawable/progress_small.xml @@ -21,5 +21,5 @@ android:drawable="@drawable/spinner_black_16" android:pivotX="50%" android:pivotY="50%" - android:framesCount="12" - android:frameDuration="100" /> + android:framesCount="48" + android:frameDuration="25" /> diff --git a/core/res/res/drawable/progress_small_titlebar.xml b/core/res/res/drawable/progress_small_titlebar.xml index 8cfba864b5b2c..5bb5cf8749dc4 100644 --- a/core/res/res/drawable/progress_small_titlebar.xml +++ b/core/res/res/drawable/progress_small_titlebar.xml @@ -21,5 +21,5 @@ android:drawable="@drawable/spinner_white_16" android:pivotX="50%" android:pivotY="50%" - android:framesCount="12" - android:frameDuration="100" /> + android:framesCount="48" + android:frameDuration="25" /> diff --git a/core/res/res/drawable/progress_small_white.xml b/core/res/res/drawable/progress_small_white.xml index 8cfba864b5b2c..5bb5cf8749dc4 100644 --- a/core/res/res/drawable/progress_small_white.xml +++ b/core/res/res/drawable/progress_small_white.xml @@ -21,5 +21,5 @@ android:drawable="@drawable/spinner_white_16" android:pivotX="50%" android:pivotY="50%" - android:framesCount="12" - android:frameDuration="100" /> + android:framesCount="48" + android:frameDuration="25" /> diff --git a/core/res/res/drawable/search_spinner.xml b/core/res/res/drawable/search_spinner.xml index 31a77c30cf2a6..b8c8b09fa882e 100644 --- a/core/res/res/drawable/search_spinner.xml +++ b/core/res/res/drawable/search_spinner.xml @@ -21,5 +21,5 @@ android:drawable="@drawable/spinner_black_20" android:pivotX="50%" android:pivotY="50%" - android:framesCount="12" - android:frameDuration="100" /> + android:framesCount="48" + android:frameDuration="25" /> diff --git a/core/res/res/drawable/stat_sys_data_usb.xml b/core/res/res/drawable/stat_sys_data_usb.xml new file mode 100644 index 0000000000000..fb1ad2a47706d --- /dev/null +++ b/core/res/res/drawable/stat_sys_data_usb.xml @@ -0,0 +1,24 @@ + + + + diff --git a/core/res/res/drawable/sym_def_app_icon.xml b/core/res/res/drawable/sym_def_app_icon.xml index 129d38a747502..38d91477fbcef 100644 --- a/core/res/res/drawable/sym_def_app_icon.xml +++ b/core/res/res/drawable/sym_def_app_icon.xml @@ -1,7 +1,19 @@ + - - - + diff --git a/core/res/res/drawable/sym_def_app_icon_background.xml b/core/res/res/drawable/sym_def_app_icon_background.xml index 17b592fe10f3c..fa5cbd9d504be 100644 --- a/core/res/res/drawable/sym_def_app_icon_background.xml +++ b/core/res/res/drawable/sym_def_app_icon_background.xml @@ -1,52 +1,14 @@ - + - - - - - - - - - - - - - - - - - - - - - + android:height="108dp" + android:viewportWidth="108" + android:viewportHeight="108"> + - diff --git a/core/res/res/drawable/sym_def_app_icon_foreground.xml b/core/res/res/drawable/sym_def_app_icon_foreground.xml new file mode 100644 index 0000000000000..68cf7c93bca51 --- /dev/null +++ b/core/res/res/drawable/sym_def_app_icon_foreground.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + diff --git a/core/res/res/drawable/toast_frame.xml b/core/res/res/drawable/toast_frame.xml index a8cdef6d05f13..34987394b2ece 100644 --- a/core/res/res/drawable/toast_frame.xml +++ b/core/res/res/drawable/toast_frame.xml @@ -17,7 +17,7 @@ --> - + diff --git a/core/res/res/layout/accessibility_button_chooser_item.xml b/core/res/res/layout/accessibility_button_chooser_item.xml index 33d6fa2862f76..70905ca19192d 100644 --- a/core/res/res/layout/accessibility_button_chooser_item.xml +++ b/core/res/res/layout/accessibility_button_chooser_item.xml @@ -45,7 +45,7 @@ android:textAppearance="?attr/textAppearanceSmall" android:textColor="?attr/textColorPrimary" android:textSize="12sp" - android:fontFamily="sans-serif-condensed" + android:fontFamily="@*android:string/config_bodyFontFamily" android:gravity="top|center_horizontal" android:minLines="2" android:maxLines="2" diff --git a/core/res/res/layout/accessibility_enable_service_warning.xml b/core/res/res/layout/accessibility_enable_service_warning.xml index 01ef10177c5a3..fc6f498375193 100644 --- a/core/res/res/layout/accessibility_enable_service_warning.xml +++ b/core/res/res/layout/accessibility_enable_service_warning.xml @@ -51,7 +51,7 @@ android:gravity="center" android:textSize="20sp" android:textColor="?android:attr/textColorPrimary" - android:fontFamily="google-sans-medium"/> + android:fontFamily="@*android:string/config_headlineFontFamily"/> + android:fontFamily="@*android:string/config_bodyFontFamily"/> + android:fontFamily="@*android:string/config_headlineFontFamily"/> + android:fontFamily="@*android:string/config_bodyFontFamily"/> + android:fontFamily="@*android:string/config_headlineFontFamily"/> + android:fontFamily="@*android:string/config_bodyFontFamily" /> + android:fontFamily="@*android:string/config_bodyFontFamily"/> + android:fontFamily="@*android:string/config_bodyFontFamily"/> diff --git a/core/res/res/layout/alert_dialog_material.xml b/core/res/res/layout/alert_dialog_material.xml index 178505c264a46..b1510fdcb93dd 100644 --- a/core/res/res/layout/alert_dialog_material.xml +++ b/core/res/res/layout/alert_dialog_material.xml @@ -54,7 +54,7 @@ android:layout_height="wrap_content" android:paddingEnd="?attr/dialogPreferredPadding" android:paddingStart="?attr/dialogPreferredPadding" - style="@style/TextAppearance.Material.Subhead" /> + style="@style/TextAppearance.DeviceDefault.Subhead" /> +