diff --git a/Android.bp b/Android.bp index bf7b5d8df1e1f..938b18f3220b6 100644 --- a/Android.bp +++ b/Android.bp @@ -140,6 +140,7 @@ filegroup { ":deviceproductinfoconstants_aidl", ":adbrootservice_aidl", + ":lmofreeform_aidl", // For the generated R.java and Manifest.java ":framework-res{.aapt.srcjar}", 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/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java index b16fdc4b4236e..4b29f71e07726 100644 --- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java +++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java @@ -3231,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(), 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 8694f9c8aa75e..28da5ddcf79f0 100644 --- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java +++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java @@ -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/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/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..e6d137749f6a8 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -247,6 +247,7 @@ import com.android.internal.util.ArrayUtils; import com.android.internal.util.FastPrintWriter; import com.android.internal.util.Preconditions; +import com.android.internal.util.crdroid.FontController; import com.android.internal.util.function.pooled.PooledLambda; import com.android.org.conscrypt.TrustedCertificateStore; import com.android.server.am.MemInfoDumpProto; @@ -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/app/AppLockData.aidl b/core/java/android/app/AppLockData.aidl new file mode 100644 index 0000000000000..073d1efd25055 --- /dev/null +++ b/core/java/android/app/AppLockData.aidl @@ -0,0 +1,19 @@ +/* + * 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; + +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/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index 7c70daa8c3e8b..b42b34f2cb0fd 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -836,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..c6a4d2ad321b4 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); } } diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index 60be285dd2628..54ec1baf718c2 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -1042,4 +1042,18 @@ 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); } 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/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/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/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/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 5db6de7c1f44b..6de3848d06d19 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -3599,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, @@ -4637,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/res/AssetManager.java b/core/java/android/content/res/AssetManager.java index a5f4fcd7bb3fe..d9695dc1f2e61 100644 --- a/core/java/android/content/res/AssetManager.java +++ b/core/java/android/content/res/AssetManager.java @@ -973,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/hardware/Camera.java b/core/java/android/hardware/Camera.java index 322d16189f802..a3e2bb6e8a9ab 100644 --- a/core/java/android/hardware/Camera.java +++ b/core/java/android/hardware/Camera.java @@ -175,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 @@ -206,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 @@ -416,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. @@ -580,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) { @@ -1226,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; @@ -1737,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); } @@ -1967,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; } /** @@ -2091,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. * @@ -2104,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. * @@ -2368,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"; @@ -2415,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. */ @@ -2491,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 @@ -2533,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 @@ -2585,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 @@ -2621,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 @@ -3445,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); @@ -3616,6 +3891,7 @@ public String getSceneMode() { * @see #getSceneMode() */ public void setSceneMode(String value) { + if(getSupportedSceneModes() == null) return; set(KEY_SCENE_MODE, value); } @@ -3653,6 +3929,7 @@ public String getFlashMode() { * @see #getFlashMode() */ public void setFlashMode(String value) { + if(getSupportedFlashModes() == null) return; set(KEY_FLASH_MODE, value); } @@ -4337,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); } } @@ -4407,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; @@ -4433,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; @@ -4470,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 42ab4db96ae65..e99c3a3555c67 100644 --- a/core/java/android/hardware/camera2/CameraManager.java +++ b/core/java/android/hardware/camera2/CameraManager.java @@ -2540,8 +2540,11 @@ private String[] extractCameraIdListLocked(int deviceId, int devicePolicy) { try { List cameraIds = new ArrayList<>(); boolean exposeAuxCamera = Camera.shouldExposeAuxCamera(); - for (int i = 0; i < mDeviceStatus.size(); i++) { - if (!exposeAuxCamera && i == 2) break; + 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 @@ -2591,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() { @@ -2865,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, @@ -3288,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/impl/CameraCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java index 618044123d7c0..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 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 545be8f104e40..9de7bcad41fa7 100644 --- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java @@ -114,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 @@ -581,6 +583,10 @@ public void run() { } } + public void setVendorStreamConfigMode(int fpsrange) { + customOpMode = fpsrange; + } + @Override public String getId() { return mCameraId; @@ -637,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; @@ -706,6 +716,7 @@ public boolean configureStreamsChecked(InputConfiguration inputConfig, mConfiguredOutputs.put(streamId, outConfig); } } + operatingMode = (operatingMode | (customOpMode << 16)); int offlineStreamIds[]; if (sessionParams != null) { 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/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/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/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/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/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 b7b5af48e0353..421443c49c765 100644 --- a/core/java/android/os/BatteryManager.java +++ b/core/java/android/os/BatteryManager.java @@ -281,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/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/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/Settings.java b/core/java/android/provider/Settings.java index 082688c47e645..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. * @@ -6598,59 +6641,803 @@ public static void setShowGTalkServiceStatusForUser(ContentResolver cr, boolean * * @hide */ - public static final String SCREEN_FLASH_NOTIFICATION = "screen_flash_notification"; + public static final String SCREEN_FLASH_NOTIFICATION = "screen_flash_notification"; + + /** + * Setting to enable CV (proprietary) + * + * @hide + */ + public static final String CV_ENABLED = + "cv_enabled"; + + /** + * Setting to set enable/disable CV dynamic mode. + * Setting should be boolean (0 or 1) + * + * @hide + */ + public static final String CV_DYNAMIC_ENABLED = + "cv_dynamic_enabled"; + + /** + * Setting to set CV preferred intensity + * Setting should be integer (0-10) + * + * @hide + */ + 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"; /** - * Setting to enable CV (proprietary) - * + * Whether Smart Pixels should enable on power saver mode * @hide */ - public static final String CV_ENABLED = - "cv_enabled"; + public static final String SMART_PIXELS_ON_POWER_SAVE = "smart_pixels_on_power_save"; /** - * Setting to set enable/disable CV dynamic mode. - * Setting should be boolean (0 or 1) - * + * Defines the screen-off animation to display * @hide */ - public static final String CV_DYNAMIC_ENABLED = - "cv_dynamic_enabled"; + public static final String SCREEN_OFF_ANIMATION = "screen_off_animation"; /** - * Setting to set CV preferred intensity - * Setting should be integer (0-10) - * + * Whether to show rotation suggestion * @hide */ - public static final String CV_PREFERRED_INTENSITY = "cv_preferred_intensity"; + @Readable + public static final String ENABLE_ROTATION_BUTTON = "enable_rotation_button"; /** - * Integer property that specifes the color for screen flash notification as a - * packed 32-bit color. - * - * @see android.graphics.Color#argb + * 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 SCREEN_FLASH_NOTIFICATION_COLOR = - "screen_flash_notification_color_global"; + public static final String ADAPTIVE_PLAYBACK_ENABLED = "adaptive_playback_enabled"; /** - * 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 + * Adaptive playback's timeout in ms * @hide */ - @Readable - public static final String VOLUME_KEY_CURSOR_CONTROL = "volume_key_cursor_control"; + 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 @@ -6700,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); @@ -6827,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); } @@ -10557,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. *

@@ -10564,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"; @@ -10572,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. @@ -10624,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 @@ -12538,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 */ @@ -12557,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. @@ -13231,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. @@ -13518,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 @@ -13745,6 +14817,7 @@ public static void setLocationProviderEnabled(ContentResolver cr, * * @hide */ + @Readable public static final String CHARGE_OPTIMIZATION_MODE = "charge_optimization_mode"; /** @@ -17404,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. */ @@ -19030,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. @@ -20239,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. * @@ -21635,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(), @@ -21656,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; } /** @@ -21706,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()); @@ -21747,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/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/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/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/LayoutInflater.java b/core/java/android/view/LayoutInflater.java index 333fd537a6f3f..a58680ea408e9 100644 --- a/core/java/android/view/LayoutInflater.java +++ b/core/java/android/view/LayoutInflater.java @@ -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) @@ -1244,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 802573733d22b..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); } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 40d6318c831f9..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; @@ -3512,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; @@ -3860,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(); } @@ -7752,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. @@ -7909,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); @@ -8391,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; } @@ -13713,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/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/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/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/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/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/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/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/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 0bca6c2efaa24..c0713dd1d16b8 100644 --- a/core/java/com/android/internal/statusbar/IStatusBar.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl @@ -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 b54bfaa28745e..aa40e92d74b72 100644 --- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl @@ -249,4 +249,11 @@ interface IStatusBarService * 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/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/FontController.java b/core/java/com/android/internal/util/crdroid/FontController.java new file mode 100644 index 0000000000000..9f1846c50a125 --- /dev/null +++ b/core/java/com/android/internal/util/crdroid/FontController.java @@ -0,0 +1,183 @@ +/* + * 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.crdroid; + +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 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 static final Set OVERRIDE_FONTS = new HashSet<>(Arrays.asList( + "google", "sans-serif", "gsf-" + )); + + private static final Set EXCLUDED_APPS = new HashSet<>(Arrays.asList( + "it.subito", + "tv.arte.plus7", + "com.google.android.gm" + )); + + 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); + } + + 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) { + if (fontToOverride == null) return null; + + String pkgName = getCurrentPackageName(); + if (pkgName != null && EXCLUDED_APPS.contains(pkgName)) { + logger("Excluded app, skipping override: " + pkgName); + return null; + } + + String currentFont = Typeface.getFontName(); + + 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); + 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) { + String pkgName = getCurrentPackageName(); + if (pkgName == null || EXCLUDED_APPS.contains(pkgName)) return; + logger("handleOnConfiguration: Changing default font to: " + Typeface.getFontName()); + Typeface.changeFont(res); + } + + private static String getCurrentPackageName() { + try { + return ActivityThread.currentPackageName(); + } catch (Exception e) { + logger("getCurrentPackageName failed: " + e.getMessage()); + return null; + } + } + + private static Resources getResources() { + try { + return Resources.getSystem(); + } catch (Exception e) { + logger("getResources failed: " + e.getMessage()); + return null; + } + } + + private static int getFontWeightAdjustment() { + try { + Resources res = getResources(); + 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) { + 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/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..7629279bb08be --- /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.251205.006"); + propsToChangePixel10ProXL.put("FINGERPRINT", "google/mustang/mustang:16/BP4A.251205.006/14401865: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.251205.006"); + propsToChangePixelTablet.put("FINGERPRINT", "google/tangorpro/tangorpro:16/BP4A.251205.006/14401865: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/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java index e2a2be596fefc..d3680623f2977 100644 --- a/core/java/com/android/internal/widget/LockPatternUtils.java +++ b/core/java/com/android/internal/widget/LockPatternUtils.java @@ -128,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 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/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 7fdece910bab8..22c333591eb4e 100644 --- a/core/jni/fd_utils.cpp +++ b/core/jni/fd_utils.cpp @@ -123,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"; @@ -136,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/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 1dd541f5baffc..7b6234d7f4896 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -831,6 +831,7 @@ + @@ -867,6 +868,14 @@ + + + + + + + + @@ -948,6 +957,10 @@ android:knownCerts="@array/config_setContactsDefaultAccountKnownSigners" android:featureFlag="android.provider.new_default_account_api_enabled"/> + + + @@ -9202,6 +9215,11 @@ android:protectionLevel="internal|role" android:featureFlag="com.android.window.flags.enable_window_repositioning_api" /> + + + @@ -9235,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/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/ic_wifi_signal_0.xml b/core/res/res/drawable/ic_wifi_signal_0.xml index cab8be3d34820..7f98457a4c768 100644 --- a/core/res/res/drawable/ic_wifi_signal_0.xml +++ b/core/res/res/drawable/ic_wifi_signal_0.xml @@ -1,25 +1,37 @@ - - - \ No newline at end of file + android:width="17.0dp" + android:height="12.58dp" + android:viewportHeight="12.0" + android:viewportWidth="16.21"> + + + + + + + + diff --git a/core/res/res/drawable/ic_wifi_signal_1.xml b/core/res/res/drawable/ic_wifi_signal_1.xml index 54b68aeaaaee3..9c661f4b5d7af 100644 --- a/core/res/res/drawable/ic_wifi_signal_1.xml +++ b/core/res/res/drawable/ic_wifi_signal_1.xml @@ -1,28 +1,36 @@ - + android:width="17.0dp" + android:height="12.58dp" + android:viewportHeight="12.0" + android:viewportWidth="16.21"> + + android:fillAlpha="0.45" + android:fillColor="#000" + android:pathData="M12.659,7.45C13.199,6.91 13.149,6.01 12.479,5.64C11.179,4.92 9.689,4.51 8.109,4.51C6.529,4.51 5.029,4.92 3.739,5.64C3.069,6.01 3.019,6.91 3.559,7.45C3.999,7.89 4.699,7.94 5.259,7.66C6.119,7.24 7.089,7 8.109,7C9.129,7 10.099,7.24 10.959,7.66C11.519,7.94 12.219,7.89 12.659,7.45Z" /> + - \ No newline at end of file + android:fillAlpha="0.45" + android:fillColor="#000" + android:pathData="M15.85,4.26C16.36,3.75 16.34,2.91 15.76,2.49C13.61,0.93 10.97,0 8.11,0C5.25,0 2.61,0.92 0.46,2.49C-0.12,2.91 -0.14,3.75 0.37,4.26C0.84,4.73 1.59,4.75 2.13,4.37C3.83,3.19 5.89,2.5 8.11,2.5C10.33,2.5 12.39,3.19 14.09,4.37C14.63,4.75 15.38,4.73 15.85,4.26Z" /> + + + + diff --git a/core/res/res/drawable/ic_wifi_signal_2.xml b/core/res/res/drawable/ic_wifi_signal_2.xml index 52f589502d39a..02c14e18a95ae 100644 --- a/core/res/res/drawable/ic_wifi_signal_2.xml +++ b/core/res/res/drawable/ic_wifi_signal_2.xml @@ -1,28 +1,35 @@ - + android:width="17.0dp" + android:height="12.58dp" + android:viewportHeight="12.0" + android:viewportWidth="16.21"> + + android:fillColor="#000" + android:pathData="M12.659,7.45C13.199,6.91 13.149,6.01 12.479,5.64C11.179,4.92 9.689,4.51 8.109,4.51C6.529,4.51 5.029,4.92 3.739,5.64C3.069,6.01 3.019,6.91 3.559,7.45C3.999,7.89 4.699,7.94 5.259,7.66C6.119,7.24 7.089,7 8.109,7C9.129,7 10.099,7.24 10.959,7.66C11.519,7.94 12.219,7.89 12.659,7.45Z" /> + - \ No newline at end of file + android:fillAlpha="0.45" + android:fillColor="#000" + android:pathData="M15.85,4.26C16.36,3.75 16.34,2.91 15.76,2.49C13.61,0.93 10.97,0 8.11,0C5.25,0 2.61,0.92 0.46,2.49C-0.12,2.91 -0.14,3.75 0.37,4.26C0.84,4.73 1.59,4.75 2.13,4.37C3.83,3.19 5.89,2.5 8.11,2.5C10.33,2.5 12.39,3.19 14.09,4.37C14.63,4.75 15.38,4.73 15.85,4.26Z" /> + + + + diff --git a/core/res/res/drawable/ic_wifi_signal_3.xml b/core/res/res/drawable/ic_wifi_signal_3.xml index 8e51ed2f53b61..02c14e18a95ae 100644 --- a/core/res/res/drawable/ic_wifi_signal_3.xml +++ b/core/res/res/drawable/ic_wifi_signal_3.xml @@ -1,28 +1,35 @@ - + android:width="17.0dp" + android:height="12.58dp" + android:viewportHeight="12.0" + android:viewportWidth="16.21"> + + android:fillColor="#000" + android:pathData="M12.659,7.45C13.199,6.91 13.149,6.01 12.479,5.64C11.179,4.92 9.689,4.51 8.109,4.51C6.529,4.51 5.029,4.92 3.739,5.64C3.069,6.01 3.019,6.91 3.559,7.45C3.999,7.89 4.699,7.94 5.259,7.66C6.119,7.24 7.089,7 8.109,7C9.129,7 10.099,7.24 10.959,7.66C11.519,7.94 12.219,7.89 12.659,7.45Z" /> + - \ No newline at end of file + android:fillAlpha="0.45" + android:fillColor="#000" + android:pathData="M15.85,4.26C16.36,3.75 16.34,2.91 15.76,2.49C13.61,0.93 10.97,0 8.11,0C5.25,0 2.61,0.92 0.46,2.49C-0.12,2.91 -0.14,3.75 0.37,4.26C0.84,4.73 1.59,4.75 2.13,4.37C3.83,3.19 5.89,2.5 8.11,2.5C10.33,2.5 12.39,3.19 14.09,4.37C14.63,4.75 15.38,4.73 15.85,4.26Z" /> + + + + diff --git a/core/res/res/drawable/ic_wifi_signal_4.xml b/core/res/res/drawable/ic_wifi_signal_4.xml index 04b63af4a372c..6b183aff40f02 100644 --- a/core/res/res/drawable/ic_wifi_signal_4.xml +++ b/core/res/res/drawable/ic_wifi_signal_4.xml @@ -1,25 +1,34 @@ - + android:width="17.0dp" + android:height="12.58dp" + android:viewportHeight="12.0" + android:viewportWidth="16.21"> + + + - \ No newline at end of file + android:fillColor="#000" + android:pathData="M15.85,4.26C16.36,3.75 16.34,2.91 15.76,2.49C13.61,0.93 10.97,0 8.11,0C5.25,0 2.61,0.92 0.46,2.49C-0.12,2.91 -0.14,3.75 0.37,4.26C0.84,4.73 1.59,4.75 2.13,4.37C3.83,3.19 5.89,2.5 8.11,2.5C10.33,2.5 12.39,3.19 14.09,4.37C14.63,4.75 15.38,4.73 15.85,4.26Z" /> + + + + 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/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" /> +