diff --git a/Android.bp b/Android.bp index df6fdaa5fdf6..487e4de0efaf 100644 --- a/Android.bp +++ b/Android.bp @@ -237,11 +237,11 @@ java_library { "android.se.omapi-V1-java", "android.system.suspend.control.internal-java", "devicepolicyprotosnano", - "com.android.sysprop.apex", "com.android.sysprop.init", "com.android.sysprop.localization", "PlatformProperties", + "vendor.lineage.touch-V1.0-java", ], sdk_version: "core_platform", installable: false, diff --git a/CleanSpec.mk b/CleanSpec.mk index 02e8eecbb721..e8ab07d64603 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/apct-tests/perftests/core/Android.bp b/apct-tests/perftests/core/Android.bp index 2182f0bc2501..96c63e9e520c 100644 --- a/apct-tests/perftests/core/Android.bp +++ b/apct-tests/perftests/core/Android.bp @@ -58,4 +58,10 @@ android_test { test_suites: ["device-tests"], certificate: "platform", + + errorprone: { + javacflags: [ + "-Xep:ReturnValueIgnored:WARN", + ], + }, } diff --git a/apct-tests/perftests/core/src/android/libcore/ReferencePerfTest.java b/apct-tests/perftests/core/src/android/libcore/ReferencePerfTest.java index 2ef68ca7bdb2..05a3e1201a00 100644 --- a/apct-tests/perftests/core/src/android/libcore/ReferencePerfTest.java +++ b/apct-tests/perftests/core/src/android/libcore/ReferencePerfTest.java @@ -118,7 +118,7 @@ public void timeFinalization() { int got = count.get(); if (n != got) { throw new IllegalStateException( - String.format("Only %i of %i objects finalized?", got, n)); + String.format("Only %d of %d objects finalized?", got, n)); } } } diff --git a/cmds/screencap/screencap.cpp b/cmds/screencap/screencap.cpp index 7e6a521ca46c..051963aca00c 100644 --- a/cmds/screencap/screencap.cpp +++ b/cmds/screencap/screencap.cpp @@ -90,6 +90,7 @@ static status_t notifyMediaScanner(const char* fileName) { (char*) "android.intent.action.MEDIA_SCANNER_SCAN_FILE", (char*) "-d", &filePath[0], + (char*) "--async", nullptr }; diff --git a/core/api/current.txt b/core/api/current.txt index c8a43db2f9c2..277183036c60 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -87,6 +87,7 @@ package android { field public static final String DUMP = "android.permission.DUMP"; field public static final String EXPAND_STATUS_BAR = "android.permission.EXPAND_STATUS_BAR"; field public static final String FACTORY_TEST = "android.permission.FACTORY_TEST"; + field public static final String FAKE_PACKAGE_SIGNATURE = "android.permission.FAKE_PACKAGE_SIGNATURE"; field public static final String FOREGROUND_SERVICE = "android.permission.FOREGROUND_SERVICE"; field public static final String GET_ACCOUNTS = "android.permission.GET_ACCOUNTS"; field public static final String GET_ACCOUNTS_PRIVILEGED = "android.permission.GET_ACCOUNTS_PRIVILEGED"; @@ -222,6 +223,7 @@ package android { field public static final String CALL_LOG = "android.permission-group.CALL_LOG"; field public static final String CAMERA = "android.permission-group.CAMERA"; field public static final String CONTACTS = "android.permission-group.CONTACTS"; + field public static final String FAKE_PACKAGE = "android.permission-group.FAKE_PACKAGE"; field public static final String LOCATION = "android.permission-group.LOCATION"; field public static final String MICROPHONE = "android.permission-group.MICROPHONE"; field public static final String NEARBY_DEVICES = "android.permission-group.NEARBY_DEVICES"; diff --git a/core/java/android/accounts/ChooseTypeAndAccountActivity.java b/core/java/android/accounts/ChooseTypeAndAccountActivity.java index f623295dee3e..6e02390bb711 100644 --- a/core/java/android/accounts/ChooseTypeAndAccountActivity.java +++ b/core/java/android/accounts/ChooseTypeAndAccountActivity.java @@ -402,7 +402,7 @@ public void run(final AccountManagerFuture accountManagerFuture) { mExistingAccounts = AccountManager.get(this).getAccountsForPackage(mCallingPackage, mCallingUid); intent.setFlags(intent.getFlags() & ~Intent.FLAG_ACTIVITY_NEW_TASK); - startActivityForResult(intent, REQUEST_ADD_ACCOUNT); + startActivityForResult(new Intent(intent), REQUEST_ADD_ACCOUNT); return; } } catch (OperationCanceledException e) { diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java index f2ea060b1694..710ae8f1a786 100644 --- a/core/java/android/app/ActivityManagerInternal.java +++ b/core/java/android/app/ActivityManagerInternal.java @@ -691,6 +691,9 @@ public abstract PendingIntent getPendingIntentActivityAsApp( */ public abstract @TempAllowListType int getPushMessagingOverQuotaBehavior(); + // Starts a process as empty. + public abstract int startActivityAsUserEmpty(Bundle options); + /** * Return the startForeground() grace period after calling startForegroundService(). */ diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 51efdbac00cc..9c0c1e9291ab 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -159,6 +159,7 @@ import android.telephony.TelephonyFrameworkInitializer; import android.util.AndroidRuntimeException; import android.util.ArrayMap; +import android.util.BoostFramework; import android.util.DisplayMetrics; import android.util.EventLog; import android.util.Log; @@ -6448,6 +6449,8 @@ private String getInstrumentationLibrary(ApplicationInfo appInfo, Instrumentatio @UnsupportedAppUsage private void handleBindApplication(AppBindData data) { + long st_bindApp = SystemClock.uptimeMillis(); + BoostFramework ux_perf = null; // Register the UI Thread as a sensitive thread to the runtime. VMRuntime.registerSensitiveThread(); // In the case the stack depth property exists, pass it down to the runtime. @@ -6566,10 +6569,17 @@ private void handleBindApplication(AppBindData data) { /** * Switch this process to density compatibility mode if needed. */ - if ((data.appInfo.flags&ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES) + if ((data.appInfo.flags & ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES) == 0) { mDensityCompatMode = true; Bitmap.setDefaultDensity(DisplayMetrics.DENSITY_DEFAULT); + } else { + int overrideDensity = data.appInfo.getOverrideDensity(); + if(overrideDensity != 0) { + Log.d(TAG, "override app density from " + DisplayMetrics.DENSITY_DEVICE + " to " + overrideDensity); + mDensityCompatMode = true; + Bitmap.setDefaultDensity(overrideDensity); + } } mConfigurationController.updateDefaultDensity(data.config.densityDpi); @@ -6667,6 +6677,15 @@ private void handleBindApplication(AppBindData data) { Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); } + if (!Process.isIsolated()) { + final int old_mask = StrictMode.allowThreadDiskWritesMask(); + try { + ux_perf = new BoostFramework(appContext); + } finally { + StrictMode.setThreadPolicyMask(old_mask); + } + } + if (!Process.isIsolated()) { final int oldMask = StrictMode.allowThreadDiskWritesMask(); try { @@ -6793,6 +6812,33 @@ private void handleBindApplication(AppBindData data) { throw e.rethrowFromSystemServer(); } } + long end_bindApp = SystemClock.uptimeMillis(); + int bindApp_dur = (int) (end_bindApp - st_bindApp); + String pkg_name = null; + if (appContext != null) { + pkg_name = appContext.getPackageName(); + } + if (ux_perf != null && !Process.isIsolated() && pkg_name != null) { + String pkgDir = null; + try + { + String codePath = appContext.getPackageCodePath(); + pkgDir = codePath.substring(0, codePath.lastIndexOf('/')); + } + catch(Exception e) + { + Slog.e(TAG, "HeavyGameThread () : Exception_1 = " + e); + } + if (ux_perf.board_first_api_lvl < BoostFramework.VENDOR_T_API_LEVEL && + ux_perf.board_api_lvl < BoostFramework.VENDOR_T_API_LEVEL) { + ux_perf.perfUXEngine_events(BoostFramework.UXE_EVENT_BINDAPP, 0, + pkg_name, + bindApp_dur, + pkgDir); + } else { + ux_perf.perfEvent(BoostFramework.VENDOR_HINT_BINDAPP, pkg_name, 2, bindApp_dur, 0); + } + } } private void handleSetContentCaptureOptionsCallback(String packageName) { @@ -7037,9 +7083,9 @@ public final IContentProvider acquireProvider( } if (holder == null) { if (UserManager.get(c).isUserUnlocked(userId)) { - Slog.e(TAG, "Failed to find provider info for " + auth); + if (DEBUG_MESSAGES) Slog.e(TAG, "Failed to find provider info for " + auth); } else { - Slog.w(TAG, "Failed to find provider info for " + auth + " (user not unlocked)"); + if (DEBUG_MESSAGES) Slog.w(TAG, "Failed to find provider info for " + auth + " (user not unlocked)"); } return null; } diff --git a/core/java/android/app/AppLockData.aidl b/core/java/android/app/AppLockData.aidl new file mode 100644 index 000000000000..073d1efd2505 --- /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 000000000000..19a72c4034fd --- /dev/null +++ b/core/java/android/app/AppLockData.java @@ -0,0 +1,89 @@ +/* + * 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 protected with 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 mShouldRedactNotification; + + /** @hide */ + public AppLockData( + @NonNull final String packageName, + final boolean shouldRedactNotification + ) { + mPackageName = packageName; + mShouldRedactNotification = shouldRedactNotification; + } + + private AppLockData(final Parcel in) { + mPackageName = in.readString(); + mShouldRedactNotification = in.readBoolean(); + } + + @NonNull + public String getPackageName() { + return mPackageName; + } + + public boolean getShouldRedactNotification() { + return mShouldRedactNotification; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(final Parcel parcel, final int flags) { + parcel.writeString(mPackageName); + parcel.writeBoolean(mShouldRedactNotification); + } + + @Override + @NonNull + public String toString() { + return "AppLockData[ packageName = " + mPackageName + + ", shouldRedactNotification = " + mShouldRedactNotification + " ]"; + } +} \ No newline at end of file diff --git a/core/java/android/app/AppLockManager.java b/core/java/android/app/AppLockManager.java new file mode 100644 index 000000000000..cfb22d57f9db --- /dev/null +++ b/core/java/android/app/AppLockManager.java @@ -0,0 +1,217 @@ +/* + * 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 + */ +@SystemApi +@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; + + /** + * 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; + } + + /** + * Add an application to be protected. Package should be an user + * installed application or a system app whitelisted in + * {@link config_appLockAllowedSystemApps}. + * Caller must hold {@link android.permission.MANAGE_APP_LOCK}. + * + * @param packageName the package name of the app to add. + */ + @UserHandleAware + @RequiresPermission(Manifest.permission.MANAGE_APP_LOCK) + public void addPackage(@NonNull String packageName) { + try { + mService.addPackage(packageName, mContext.getUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Remove an application from the protected packages list. + * Caller must hold {@link android.permission.MANAGE_APP_LOCK}. + * + * @param packageName the package name of the app to remove. + */ + @UserHandleAware + @RequiresPermission(Manifest.permission.MANAGE_APP_LOCK) + public void removePackage(@NonNull String packageName) { + try { + mService.removePackage(packageName, 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. + */ + @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. + */ + @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 secure true to hide notification content. + */ + @UserHandleAware + @RequiresPermission(Manifest.permission.MANAGE_APP_LOCK) + public void setShouldRedactNotification(@NonNull String packageName, boolean secure) { + try { + mService.setShouldRedactNotification(packageName, secure, 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. + */ + @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 otheriwse. + */ + @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. + */ + @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(); + } + } +} \ No newline at end of file diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index cb64173b7809..9344d96d2893 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -9134,8 +9134,9 @@ public int startProxyOpNoThrow(@NonNull String op, int proxiedUid, */ public int startProxyOpNoThrow(int op, @NonNull AttributionSource attributionSource, @Nullable String message, boolean skipProxyOperation) { - return startProxyOpNoThrow(op, attributionSource, message, skipProxyOperation, - ATTRIBUTION_FLAGS_NONE, ATTRIBUTION_FLAGS_NONE, ATTRIBUTION_CHAIN_ID_NONE); + return startProxyOpNoThrow(attributionSource.getToken(), op, attributionSource, message, + skipProxyOperation, ATTRIBUTION_FLAGS_NONE, ATTRIBUTION_FLAGS_NONE, + ATTRIBUTION_CHAIN_ID_NONE); } /** @@ -9147,7 +9148,8 @@ public int startProxyOpNoThrow(int op, @NonNull AttributionSource attributionSou * * @hide */ - public int startProxyOpNoThrow(int op, @NonNull AttributionSource attributionSource, + public int startProxyOpNoThrow(@NonNull IBinder clientId, int op, + @NonNull AttributionSource attributionSource, @Nullable String message, boolean skipProxyOperation, @AttributionFlags int proxyAttributionFlags, @AttributionFlags int proxiedAttributionFlags, int attributionChainId) { @@ -9165,7 +9167,7 @@ public int startProxyOpNoThrow(int op, @NonNull AttributionSource attributionSou } } - SyncNotedAppOp syncOp = mService.startProxyOperation(op, + SyncNotedAppOp syncOp = mService.startProxyOperation(clientId, op, attributionSource, false, collectionMode == COLLECT_ASYNC, message, shouldCollectMessage, skipProxyOperation, proxyAttributionFlags, proxiedAttributionFlags, attributionChainId); @@ -9263,9 +9265,10 @@ public void finishOp(IBinder token, int op, int uid, @NonNull String packageName */ public void finishProxyOp(@NonNull String op, int proxiedUid, @NonNull String proxiedPackageName, @Nullable String proxiedAttributionTag) { - finishProxyOp(op, new AttributionSource(mContext.getAttributionSource(), + IBinder token = mContext.getAttributionSource().getToken(); + finishProxyOp(token, op, new AttributionSource(mContext.getAttributionSource(), new AttributionSource(proxiedUid, proxiedPackageName, proxiedAttributionTag, - mContext.getAttributionSource().getToken())), /*skipProxyOperation*/ false); + token)), /*skipProxyOperation*/ false); } /** @@ -9280,10 +9283,11 @@ public void finishProxyOp(@NonNull String op, int proxiedUid, * * @hide */ - public void finishProxyOp(@NonNull String op, @NonNull AttributionSource attributionSource, - boolean skipProxyOperation) { + public void finishProxyOp(@NonNull IBinder clientId, @NonNull String op, + @NonNull AttributionSource attributionSource, boolean skipProxyOperation) { try { - mService.finishProxyOperation(strOpToOp(op), attributionSource, skipProxyOperation); + mService.finishProxyOperation(clientId, strOpToOp(op), attributionSource, + skipProxyOperation); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/app/AppOpsManagerInternal.java b/core/java/android/app/AppOpsManagerInternal.java index 4d6e4aedba66..43023fe9c2ab 100644 --- a/core/java/android/app/AppOpsManagerInternal.java +++ b/core/java/android/app/AppOpsManagerInternal.java @@ -26,13 +26,11 @@ import android.util.SparseIntArray; import com.android.internal.app.IAppOpsCallback; -import com.android.internal.util.function.DecFunction; import com.android.internal.util.function.HeptFunction; import com.android.internal.util.function.HexFunction; import com.android.internal.util.function.QuadFunction; import com.android.internal.util.function.QuintConsumer; import com.android.internal.util.function.QuintFunction; -import com.android.internal.util.function.TriFunction; import com.android.internal.util.function.UndecFunction; /** @@ -135,6 +133,7 @@ SyncNotedAppOp startOperation(IBinder token, int code, int uid, /** * Allows overriding start proxy operation behavior. * + * @param clientId The client calling start, represented by an IBinder * @param code The op code to start. * @param attributionSource The permission identity of the caller. * @param startIfModeDefault Whether to start the op of the mode is default. @@ -148,11 +147,12 @@ SyncNotedAppOp startOperation(IBinder token, int code, int uid, * @param superImpl The super implementation. * @return The app op note result. */ - SyncNotedAppOp startProxyOperation(int code, @NonNull AttributionSource attributionSource, - boolean startIfModeDefault, boolean shouldCollectAsyncNotedOp, String message, - boolean shouldCollectMessage, boolean skipProxyOperation, @AttributionFlags - int proxyAttributionFlags, @AttributionFlags int proxiedAttributionFlags, - int attributionChainId, @NonNull DecFunction superImpl); @@ -176,10 +176,15 @@ default void finishOperation(IBinder clientId, int code, int uid, String package * * @param code The op code to finish. * @param attributionSource The permission identity of the caller. + * @param skipProxyOperation Whether to skip the proxy in the proxy/proxied operation + * @param clientId The client calling finishProxyOperation + * @param superImpl The "standard" implementation to potentially call */ - void finishProxyOperation(int code, @NonNull AttributionSource attributionSource, + void finishProxyOperation(@NonNull IBinder clientId, int code, + @NonNull AttributionSource attributionSource, boolean skipProxyOperation, - @NonNull TriFunction superImpl); + @NonNull QuadFunction superImpl); } /** diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index d641a3b469a6..4b82c671896f 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -806,8 +806,41 @@ public Boolean recompute(HasSystemFeatureQuery query) { } }; + private static final String[] featuresPixel = { + "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_2022_EXPERIENCE", + "com.google.android.feature.PIXEL_2022_MIDYEAR_EXPERIENCE", + "com.google.android.feature.PIXEL_2021_EXPERIENCE", + "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" + }; + + private static final String[] featuresNexus = { + "com.google.android.apps.photos.NEXUS_PRELOAD", + "com.google.android.apps.photos.nexus_preload" + }; + @Override public boolean hasSystemFeature(String name, int version) { + String packageName = ActivityThread.currentPackageName(); + if (packageName != null && + packageName.equals("com.google.android.apps.photos") && + SystemProperties.getBoolean("persist.sys.pixelprops.gphotos", true)) { + if (Arrays.asList(featuresPixel).contains(name)) return false; + if (Arrays.asList(featuresNexus).contains(name)) return true; + } + if (Arrays.asList(featuresPixel).contains(name)) return true; return mHasSystemFeatureCache.query(new HasSystemFeatureQuery(name, version)); } diff --git a/core/java/android/app/BroadcastOptions.java b/core/java/android/app/BroadcastOptions.java index f0e14483d98a..aa5fa5b19117 100644 --- a/core/java/android/app/BroadcastOptions.java +++ b/core/java/android/app/BroadcastOptions.java @@ -528,6 +528,28 @@ public boolean isAlarmBroadcast() { return mIsAlarmBroadcast; } + /** + * Did this broadcast originate from a push message from the server? + * + * @return true if this broadcast is a push message, false otherwise. + * @hide + */ + public boolean isPushMessagingBroadcast() { + return mTemporaryAppAllowlistReasonCode == PowerExemptionManager.REASON_PUSH_MESSAGING; + } + + /** + * Did this broadcast originate from a push message from the server which was over the allowed + * quota? + * + * @return true if this broadcast is a push message over quota, false otherwise. + * @hide + */ + public boolean isPushMessagingOverQuotaBroadcast() { + return mTemporaryAppAllowlistReasonCode + == PowerExemptionManager.REASON_PUSH_MESSAGING_OVER_QUOTA; + } + /** {@hide} */ public long getRequireCompatChangeId() { return mRequireCompatChangeId; diff --git a/core/java/android/app/ConfigurationController.java b/core/java/android/app/ConfigurationController.java index 18dc1ce18baf..5b008207d160 100644 --- a/core/java/android/app/ConfigurationController.java +++ b/core/java/android/app/ConfigurationController.java @@ -28,6 +28,9 @@ import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.HardwareRenderer; +import android.graphics.Typeface; +import android.inputmethodservice.InputMethodService; +import android.os.Build; import android.os.LocaleList; import android.os.Trace; import android.util.DisplayMetrics; @@ -179,6 +182,7 @@ void handleConfigurationChanged(@Nullable Configuration config, final Application app = mActivityThread.getApplication(); final Resources appResources = app.getResources(); + Typeface.updateDefaultFont(appResources); mResourcesManager.applyConfigurationToResources(config, compat); updateLocaleListFromAppContext(app.getApplicationContext()); diff --git a/core/java/android/app/DownloadManager.java b/core/java/android/app/DownloadManager.java index 355092378279..761924829029 100644 --- a/core/java/android/app/DownloadManager.java +++ b/core/java/android/app/DownloadManager.java @@ -301,6 +301,13 @@ public class DownloadManager { */ public final static int PAUSED_UNKNOWN = 4; + /** + * Value of {@link #COLUMN_REASON} when the download is paused manually. + * + * @hide + */ + public final static int PAUSED_MANUAL = 5; + /** * Broadcast intent action sent by the download manager when a download completes. */ @@ -994,6 +1001,7 @@ Cursor runQuery(ContentResolver resolver, String[] projection, Uri baseUri) { parts.add(statusClause("=", Downloads.Impl.STATUS_WAITING_TO_RETRY)); parts.add(statusClause("=", Downloads.Impl.STATUS_WAITING_FOR_NETWORK)); parts.add(statusClause("=", Downloads.Impl.STATUS_QUEUED_FOR_WIFI)); + parts.add(statusClause("=", Downloads.Impl.STATUS_PAUSED_MANUAL)); } if ((mStatusFlags & STATUS_SUCCESSFUL) != 0) { parts.add(statusClause("=", Downloads.Impl.STATUS_SUCCESS)); @@ -1283,6 +1291,34 @@ public void forceDownload(long... ids) { mResolver.update(mBaseUri, values, getWhereClauseForIds(ids), getWhereArgsForIds(ids)); } + /** + * Pause the given running download manually. + * + * @param id the ID of the download to be paused + * @return the number of downloads actually updated + * @hide + */ + public int pauseDownload(long id) { + ContentValues values = new ContentValues(); + values.put(Downloads.Impl.COLUMN_STATUS, Downloads.Impl.STATUS_PAUSED_MANUAL); + + return mResolver.update(ContentUris.withAppendedId(mBaseUri, id), values, null, null); + } + + /** + * Resume the given paused download manually. + * + * @param id the ID of the download to be resumed + * @return the number of downloads actually updated + * @hide + */ + public int resumeDownload(long id) { + ContentValues values = new ContentValues(); + values.put(Downloads.Impl.COLUMN_STATUS, Downloads.Impl.STATUS_RUNNING); + + return mResolver.update(ContentUris.withAppendedId(mBaseUri, id), values, null, null); + } + /** * Returns maximum size, in bytes, of downloads that may go over a mobile connection; or null if * there's no limit @@ -1773,6 +1809,9 @@ private long getPausedReason(int status) { case Downloads.Impl.STATUS_QUEUED_FOR_WIFI: return PAUSED_QUEUED_FOR_WIFI; + case Downloads.Impl.STATUS_PAUSED_MANUAL: + return PAUSED_MANUAL; + default: return PAUSED_UNKNOWN; } @@ -1828,6 +1867,7 @@ private int translateStatus(int status) { case Downloads.Impl.STATUS_WAITING_TO_RETRY: case Downloads.Impl.STATUS_WAITING_FOR_NETWORK: case Downloads.Impl.STATUS_QUEUED_FOR_WIFI: + case Downloads.Impl.STATUS_PAUSED_MANUAL: return STATUS_PAUSED; case Downloads.Impl.STATUS_SUCCESS: diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index 8367441b1b95..c7df9c33a8f5 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -760,4 +760,9 @@ interface IActivityManager { *

*/ int getBackgroundRestrictionExemptionReason(int uid); + + /** + * Should disable touch if three fingers to screen shot is active? + */ + boolean isSwipeToScreenshotGestureActive(); } diff --git a/core/java/android/app/IAppLockManagerService.aidl b/core/java/android/app/IAppLockManagerService.aidl new file mode 100644 index 000000000000..f1b25a206686 --- /dev/null +++ b/core/java/android/app/IAppLockManagerService.aidl @@ -0,0 +1,44 @@ +/* + * 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 addPackage(in String packageName, in int userId); + + void removePackage(in String packageName, 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); +} \ No newline at end of file diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl index da6a551175e3..a24d61eb1f4e 100644 --- a/core/java/android/app/INotificationManager.aidl +++ b/core/java/android/app/INotificationManager.aidl @@ -236,4 +236,7 @@ interface INotificationManager void migrateNotificationFilter(in INotificationListener token, int defaultTypes, in List disallowedPkgs); void setToastRateLimitingEnabled(boolean enable); + + void forceShowLedLight(int color); + void forcePulseLedLight(int color, int onTime, int offTime); } diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java index 556058b567f9..f9c9f0d77fe0 100644 --- a/core/java/android/app/Instrumentation.java +++ b/core/java/android/app/Instrumentation.java @@ -64,6 +64,8 @@ import java.util.List; import java.util.concurrent.TimeoutException; +import com.android.internal.util.custom.PixelPropsUtils; + /** * Base class for implementing application instrumentation code. When running * with instrumentation turned on, this class will be instantiated for you @@ -1242,6 +1244,8 @@ public Application newApplication(ClassLoader cl, String className, Context cont Application app = getFactory(context.getPackageName()) .instantiateApplication(cl, className); app.attach(context); + String packageName = context.getPackageName(); + PixelPropsUtils.setProps(packageName); return app; } @@ -1259,6 +1263,8 @@ static public Application newApplication(Class clazz, Context context) ClassNotFoundException { Application app = (Application)clazz.newInstance(); app.attach(context); + String packageName = context.getPackageName(); + PixelPropsUtils.setProps(packageName); return app; } diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java index 392f52a08fb5..df7c1a1efe4f 100644 --- a/core/java/android/app/NotificationManager.java +++ b/core/java/android/app/NotificationManager.java @@ -2632,4 +2632,24 @@ public static int zenModeFromInterruptionFilter(int interruptionFilter, int defV default: return defValue; } } + + /** @hide */ + public void forceShowLedLight(int color) { + final INotificationManager service = getService(); + try { + service.forceShowLedLight(color); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** @hide */ + public void forcePulseLedLight(int color, int onTime, int offTime) { + final INotificationManager service = getService(); + try { + service.forcePulseLedLight(color, onTime, offTime); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } } diff --git a/core/java/android/app/SearchableInfo.java b/core/java/android/app/SearchableInfo.java index 5388282a1b46..bd5d1057f5bf 100644 --- a/core/java/android/app/SearchableInfo.java +++ b/core/java/android/app/SearchableInfo.java @@ -396,6 +396,17 @@ public static class ActionKeyInfo implements Parcelable { private final String mSuggestActionMsg; private final String mSuggestActionMsgColumn; + public static final Parcelable.Creator CREATOR = + new Parcelable.Creator() { + public ActionKeyInfo createFromParcel(Parcel in) { + return new ActionKeyInfo(in); + } + + public ActionKeyInfo[] newArray(int size) { + return new ActionKeyInfo[size]; + } + }; + /** * Create one object using attributeset as input data. * @param activityContext runtime context of the activity that the action key information diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index b6189692107e..65ea04f15939 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -194,6 +194,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.safetycenter.SafetyCenterFrameworkInitializer; @@ -212,7 +214,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; @@ -245,6 +246,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; @@ -264,10 +266,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; @@ -953,6 +955,15 @@ public TvInteractiveAppManager createService(ContextImpl ctx) return new TvInteractiveAppManager(service, ctx.getUserId()); }}); + registerService(Context.POCKET_SERVICE, PocketManager.class, + new CachedServiceFetcher() { + @Override + public PocketManager createService(ContextImpl ctx) { + IBinder binder = ServiceManager.getService(Context.POCKET_SERVICE); + IPocketService service = IPocketService.Stub.asInterface(binder); + return new PocketManager(ctx.getOuterContext(), service); + }}); + registerService(Context.TV_INPUT_SERVICE, TvInputManager.class, new CachedServiceFetcher() { @Override @@ -1514,6 +1525,17 @@ public AmbientContextManager createService(ContextImpl ctx) return new AmbientContextManager(ctx.getOuterContext(), manager); }}); + 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)); + }}); + sInitializing = true; try { // Note: the following functions need to be @SystemApis, once they become mainline diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 34c91c360dbe..64f3c3928cc3 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -3553,7 +3553,7 @@ public void onInstallUpdateError( * Maximum supported password length. Kind-of arbitrary. * @hide */ - public static final int MAX_PASSWORD_LENGTH = 16; + public static final int MAX_PASSWORD_LENGTH = 64; /** * Service Action: Service implemented by a device owner or profile owner supervision app to diff --git a/core/java/android/app/admin/SystemUpdateInfo.java b/core/java/android/app/admin/SystemUpdateInfo.java index b88bf76c96ca..0a22e4c66972 100644 --- a/core/java/android/app/admin/SystemUpdateInfo.java +++ b/core/java/android/app/admin/SystemUpdateInfo.java @@ -132,7 +132,7 @@ public void writeToXml(TypedXmlSerializer out, String tag) throws IOException { out.startTag(null, tag); out.attributeLong(null, ATTR_RECEIVED_TIME, mReceivedTime); out.attributeInt(null, ATTR_SECURITY_PATCH_STATE, mSecurityPatchState); - out.attribute(null, ATTR_ORIGINAL_BUILD , Build.FINGERPRINT); + out.attribute(null, ATTR_ORIGINAL_BUILD , String.valueOf(Build.TIME)); out.endTag(null, tag); } @@ -141,7 +141,7 @@ public void writeToXml(TypedXmlSerializer out, String tag) throws IOException { public static SystemUpdateInfo readFromXml(TypedXmlPullParser parser) { // If an OTA has been applied (build fingerprint has changed), discard stale info. final String buildFingerprint = parser.getAttributeValue(null, ATTR_ORIGINAL_BUILD ); - if (!Build.FINGERPRINT.equals(buildFingerprint)) { + if (!String.valueOf(Build.TIME).equals(buildFingerprint)) { return null; } try { diff --git a/core/java/android/content/ActivityNotFoundException.java b/core/java/android/content/ActivityNotFoundException.java index 16149bbc7012..5b50189015af 100644 --- a/core/java/android/content/ActivityNotFoundException.java +++ b/core/java/android/content/ActivityNotFoundException.java @@ -31,5 +31,4 @@ public ActivityNotFoundException(String name) { super(name); } -}; - +} diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index fce23cf6819a..030e901585b1 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -38,6 +38,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.IApplicationThread; @@ -6048,6 +6049,23 @@ public abstract boolean startInstrumentation(@NonNull ComponentName className, @SystemApi public static final String AMBIENT_CONTEXT_SERVICE = "ambient_context"; + /** + * {@link AppLockManager}. + * + * @see #getSystemService(String) + */ + 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"; + /** * Determine whether the given permission is allowed for a particular * process and user ID running in the system. diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 5ca8b0535dce..e4cf0a8be0cc 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -4126,6 +4126,13 @@ public static Intent createChooser(Intent target, CharSequence title, IntentSend public static final String ACTION_PROFILE_INACCESSIBLE = "android.intent.action.PROFILE_INACCESSIBLE"; + /** + * Broadcast sent to the parallel owner user when parallel space info has been refreshed. + * @hide + */ + public static final String ACTION_PARALLEL_SPACE_CHANGED = + "android.intent.action.PARALLEL_SPACE_CHANGED"; + /** * Broadcast sent to the system user when the 'device locked' state changes for any user. * Carries an extra {@link #EXTRA_USER_HANDLE} that specifies the ID of the user for which @@ -5078,6 +5085,15 @@ public static Intent createChooser(Intent target, CharSequence title, IntentSend public static final String ACTION_SHOW_FOREGROUND_SERVICE_MANAGER = "android.intent.action.SHOW_FOREGROUND_SERVICE_MANAGER"; + /** + * Broadcast action: notify the system that the user has performed a gesture on the screen + * to launch the camera. Broadcast should be protected to receivers holding the + * {@link Manifest.permission#STATUS_BAR_SERVICE} permission. + * @hide + */ + public static final String ACTION_SCREEN_CAMERA_GESTURE = + "android.intent.action.SCREEN_CAMERA_GESTURE"; + // --------------------------------------------------------------------- // --------------------------------------------------------------------- // Standard intent categories (see addCategory()). diff --git a/core/java/android/content/integrity/AtomicFormula.java b/core/java/android/content/integrity/AtomicFormula.java index f888813135be..1b5f64c456a1 100644 --- a/core/java/android/content/integrity/AtomicFormula.java +++ b/core/java/android/content/integrity/AtomicFormula.java @@ -261,8 +261,8 @@ public boolean equals(@Nullable Object o) { } LongAtomicFormula that = (LongAtomicFormula) o; return getKey() == that.getKey() - && mValue == that.mValue - && mOperator == that.mOperator; + && Objects.equals(mValue, that.mValue) + && Objects.equals(mOperator, that.mOperator); } @Override @@ -628,7 +628,7 @@ public boolean equals(@Nullable Object o) { return false; } BooleanAtomicFormula that = (BooleanAtomicFormula) o; - return getKey() == that.getKey() && mValue == that.mValue; + return getKey() == that.getKey() && Objects.equals(mValue, that.mValue); } @Override diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index 24c383692c09..ec22ae86d0ee 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -30,6 +30,8 @@ import android.graphics.drawable.Drawable; import android.os.Build; import android.os.Environment; +import android.os.SystemProperties; +import android.util.DisplayMetrics; import android.os.Parcel; import android.os.Parcelable; import android.os.SystemClock; @@ -867,6 +869,19 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { */ public static final String METADATA_PRELOADED_FONTS = "preloaded_fonts"; + /** + * Boolean indicating whether the resolution of the SurfaceView associated + * with this appplication can be overriden. + * {@hide} + */ + public int overrideRes = 0; + + /** + * In case, app needs different density than device density, set this value. + * {@hide} + */ + public int overrideDensity = 0; + /** * The required smallest screen width the application can run on. If 0, * nothing has been specified. Comes from @@ -1880,6 +1895,8 @@ public ApplicationInfo(ApplicationInfo orig) { flags = orig.flags; privateFlags = orig.privateFlags; privateFlagsExt = orig.privateFlagsExt; + overrideRes = orig.overrideRes; + overrideDensity = orig.overrideDensity; requiresSmallestWidthDp = orig.requiresSmallestWidthDp; compatibleWidthLimitDp = orig.compatibleWidthLimitDp; largestWidthLimitDp = orig.largestWidthLimitDp; @@ -1967,6 +1984,8 @@ public void writeToParcel(Parcel dest, int parcelableFlags) { dest.writeInt(flags); dest.writeInt(privateFlags); dest.writeInt(privateFlagsExt); + dest.writeInt(overrideRes); + dest.writeInt(overrideDensity); dest.writeInt(requiresSmallestWidthDp); dest.writeInt(compatibleWidthLimitDp); dest.writeInt(largestWidthLimitDp); @@ -2070,6 +2089,8 @@ private ApplicationInfo(Parcel source) { flags = source.readInt(); privateFlags = source.readInt(); privateFlagsExt = source.readInt(); + overrideRes = source.readInt(); + overrideDensity = source.readInt(); requiresSmallestWidthDp = source.readInt(); compatibleWidthLimitDp = source.readInt(); largestWidthLimitDp = source.readInt(); @@ -2622,6 +2643,11 @@ public String[] getAllApkPaths() { return output.toArray(new String[output.size()]); } + /** @hide */ + public int getOverrideDensity() { + return overrideDensity; + } + /** {@hide} */ public void setCodePath(String codePath) { scanSourceDir = codePath; } /** {@hide} */ public void setBaseCodePath(String baseCodePath) { sourceDir = baseCodePath; } /** {@hide} */ public void setSplitCodePaths(String[] splitCodePaths) { splitSourceDirs = splitCodePaths; } @@ -2637,6 +2663,7 @@ public String[] getAllApkPaths() { public void setRequestRawExternalStorageAccess(@Nullable Boolean value) { requestRawExternalStorageAccess = value; } + /** {@hide} */ public void setOverrideRes(int overrideResolution) { overrideRes = overrideResolution; } /** * Replaces {@link #mAppClassNamesByProcess}. This takes over the ownership of the passed map. @@ -2672,7 +2699,6 @@ public void setAppClassNamesByProcess(@Nullable ArrayMap value) public int getMemtagMode() { return memtagMode; } - /** * Returns whether the application has requested automatic zero-initialization of native heap * memory allocations to be enabled or disabled. @@ -2769,4 +2795,6 @@ public void setEnableOnBackInvokedCallback(boolean isEnable) { privateFlagsExt &= ~PRIVATE_FLAG_EXT_ENABLE_ON_BACK_INVOKED_CALLBACK; } } + + /** {@hide} */ public int canOverrideRes() { return overrideRes; } } diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index f224f8a189f1..c9f149187387 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -189,12 +189,12 @@ public static final class Property implements Parcelable { @VisibleForTesting public Property(@NonNull String name, int type, @NonNull String packageName, @Nullable String className) { - assert name != null; - assert type >= TYPE_BOOLEAN && type <= TYPE_STRING; - assert packageName != null; - this.mName = name; + if (type < TYPE_BOOLEAN || type > TYPE_STRING) { + throw new IllegalArgumentException("Invalid type"); + } + this.mName = Objects.requireNonNull(name); this.mType = type; - this.mPackageName = packageName; + this.mPackageName = Objects.requireNonNull(packageName); this.mClassName = className; } /** @hide */ @@ -442,9 +442,8 @@ public static final class ComponentEnabledSetting implements Parcelable { */ public ComponentEnabledSetting(@NonNull ComponentName componentName, @EnabledState int newState, @EnabledFlags int flags) { - Objects.nonNull(componentName); mPackageName = null; - mComponentName = componentName; + mComponentName = Objects.requireNonNull(componentName); mEnabledState = newState; mEnabledFlags = flags; } @@ -460,8 +459,7 @@ public ComponentEnabledSetting(@NonNull ComponentName componentName, */ public ComponentEnabledSetting(@NonNull String packageName, @EnabledState int newState, @EnabledFlags int flags) { - Objects.nonNull(packageName); - mPackageName = packageName; + mPackageName = Objects.requireNonNull(packageName); mComponentName = null; mEnabledState = newState; mEnabledFlags = flags; diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index c15b3e0b80c3..2333bd9e7818 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -3637,11 +3637,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, diff --git a/core/java/android/content/pm/ShortcutInfo.java b/core/java/android/content/pm/ShortcutInfo.java index 52774e354c90..61388977662d 100644 --- a/core/java/android/content/pm/ShortcutInfo.java +++ b/core/java/android/content/pm/ShortcutInfo.java @@ -50,6 +50,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.Preconditions; +import java.lang.IllegalArgumentException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; @@ -1359,7 +1360,9 @@ public Builder setIntent(@NonNull Intent intent) { @NonNull public Builder setIntents(@NonNull Intent[] intents) { Objects.requireNonNull(intents, "intents cannot be null"); - Objects.requireNonNull(intents.length, "intents cannot be empty"); + if (intents.length == 0) { + throw new IllegalArgumentException("intents cannot be empty"); + } for (Intent intent : intents) { Objects.requireNonNull(intent, "intents cannot contain null"); Objects.requireNonNull(intent.getAction(), "intent's action must be set"); @@ -1397,7 +1400,9 @@ public Builder setPerson(@NonNull Person person) { @NonNull public Builder setPersons(@NonNull Person[] persons) { Objects.requireNonNull(persons, "persons cannot be null"); - Objects.requireNonNull(persons.length, "persons cannot be empty"); + if (persons.length == 0) { + throw new IllegalArgumentException("persons cannot be empty"); + } for (Person person : persons) { Objects.requireNonNull(person, "persons cannot contain null"); } diff --git a/core/java/android/content/pm/UserInfo.java b/core/java/android/content/pm/UserInfo.java index 9baa6ba2fb49..fa430bb99ec4 100644 --- a/core/java/android/content/pm/UserInfo.java +++ b/core/java/android/content/pm/UserInfo.java @@ -158,6 +158,12 @@ public class UserInfo implements Parcelable { */ public static final int FLAG_EPHEMERAL_ON_CREATE = 0x00002000; + /** + * Indicates that this user is a parallel space. + * Use the flag space in reverse order to make sure it won't be broke in future updates. + */ + public static final int FLAG_PARALLEL = 0x80000000; + /** * @hide */ @@ -175,7 +181,8 @@ public class UserInfo implements Parcelable { FLAG_FULL, FLAG_SYSTEM, FLAG_PROFILE, - FLAG_EPHEMERAL_ON_CREATE + FLAG_EPHEMERAL_ON_CREATE, + FLAG_PARALLEL }) @Retention(RetentionPolicy.SOURCE) public @interface UserInfoFlag { @@ -214,6 +221,14 @@ public class UserInfo implements Parcelable { public int profileGroupId; public int restrictedProfileParentId; + /** + * If this user is a parallel space, then we should point to its parent user here. + * Some unwanted features are bound to the `profileGroupId` above, so it's better + * to save the parent here in a new variable. + * @hide + */ + public int parallelParentId; + /** * Index for distinguishing different profiles with the same parent and user type for the * purpose of badging. @@ -274,6 +289,7 @@ public UserInfo(int id, String name, String iconPath, int flags, String userType this.iconPath = iconPath; this.profileGroupId = NO_PROFILE_GROUP_ID; this.restrictedProfileParentId = NO_PROFILE_GROUP_ID; + this.parallelParentId = UserHandle.USER_NULL; } /** @@ -343,6 +359,10 @@ public boolean isCloneProfile() { return UserManager.isUserTypeCloneProfile(userType); } + public boolean isParallel() { + return (flags & FLAG_PARALLEL) != 0; + } + @UnsupportedAppUsage public boolean isEnabled() { return (flags & FLAG_DISABLED) != FLAG_DISABLED; @@ -399,7 +419,7 @@ public boolean supportsSwitchTo() { // Don't support switching to pre-created users until they become "real" users. return false; } - return !isProfile(); + return !isProfile() && !isParallel(); } /** @@ -449,6 +469,7 @@ public UserInfo(UserInfo orig) { convertedFromPreCreated = orig.convertedFromPreCreated; profileGroupId = orig.profileGroupId; restrictedProfileParentId = orig.restrictedProfileParentId; + parallelParentId = orig.parallelParentId; guestToRemove = orig.guestToRemove; profileBadge = orig.profileBadge; } @@ -506,6 +527,7 @@ public void writeToParcel(Parcel dest, int parcelableFlags) { dest.writeBoolean(guestToRemove); dest.writeInt(restrictedProfileParentId); dest.writeInt(profileBadge); + dest.writeInt(parallelParentId); } @UnsupportedAppUsage @@ -535,5 +557,6 @@ private UserInfo(Parcel source) { guestToRemove = source.readBoolean(); restrictedProfileParentId = source.readInt(); profileBadge = source.readInt(); + parallelParentId = source.readInt(); } } diff --git a/core/java/android/content/res/CompatibilityInfo.java b/core/java/android/content/res/CompatibilityInfo.java index e146bb9e20e2..9d24a00b1478 100644 --- a/core/java/android/content/res/CompatibilityInfo.java +++ b/core/java/android/content/res/CompatibilityInfo.java @@ -34,6 +34,7 @@ import android.view.MotionEvent; import android.view.WindowManager; import android.view.WindowManager.LayoutParams; +import android.util.Log; /** * CompatibilityInfo class keeps the information about the screen compatibility mode that the @@ -47,6 +48,8 @@ public class CompatibilityInfo implements Parcelable { public static final CompatibilityInfo DEFAULT_COMPATIBILITY_INFO = new CompatibilityInfo() { }; + static final String TAG = "CompatibilityInfo"; + /** * This is the number of pixels we would like to have along the * short axis of an app that needs to run on a normal size screen. @@ -163,11 +166,18 @@ public CompatibilityInfo(ApplicationInfo appInfo, int screenLayout, int sw, // Let the user decide. compatFlags |= NEEDS_SCREEN_COMPAT; } - - // Modern apps always support densities. - applicationDensity = DisplayMetrics.DENSITY_DEVICE; - applicationScale = 1.0f; - applicationInvertedScale = 1.0f; + int density = appInfo.getOverrideDensity(); + if(density != 0) { + applicationDensity = density; + applicationScale = DisplayMetrics.DENSITY_DEVICE / (float) applicationDensity; + applicationInvertedScale = 1.0f / applicationScale; + compatFlags |= SCALING_REQUIRED; + } else { + // Modern apps always support densities. + applicationDensity = DisplayMetrics.DENSITY_DEVICE; + applicationScale = 1.0f; + applicationInvertedScale = 1.0f; + } } else { /** @@ -254,26 +264,31 @@ public CompatibilityInfo(ApplicationInfo appInfo, int screenLayout, int sw, compatFlags |= NEVER_NEEDS_COMPAT; } - if (overrideScale != 1.0f) { + int density = appInfo.getOverrideDensity(); + if ((appInfo.flags & ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES) == 0) { + applicationDensity = DisplayMetrics.DENSITY_DEFAULT; + applicationScale = DisplayMetrics.DENSITY_DEVICE + / (float) DisplayMetrics.DENSITY_DEFAULT; + applicationInvertedScale = 1.0f / applicationScale; + compatFlags |= SCALING_REQUIRED; + } else if((density != 0) || (overrideScale != 1.0f)) { applicationScale = overrideScale; applicationInvertedScale = 1.0f / overrideScale; applicationDensity = (int) ((DisplayMetrics.DENSITY_DEVICE_STABLE * applicationInvertedScale) + .5f); compatFlags |= HAS_OVERRIDE_SCALING; - } else if ((appInfo.flags & ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES) != 0) { + } else { applicationDensity = DisplayMetrics.DENSITY_DEVICE; applicationScale = 1.0f; applicationInvertedScale = 1.0f; - } else { - applicationDensity = DisplayMetrics.DENSITY_DEFAULT; - applicationScale = DisplayMetrics.DENSITY_DEVICE - / (float) DisplayMetrics.DENSITY_DEFAULT; - applicationInvertedScale = 1.0f / applicationScale; - compatFlags |= SCALING_REQUIRED; } } mCompatibilityFlags = compatFlags; + + Log.d(TAG, "mCompatibilityFlags - " + Integer.toHexString(mCompatibilityFlags)); + Log.d(TAG, "applicationDensity - " + applicationDensity); + Log.d(TAG, "applicationScale - " + applicationScale); } private CompatibilityInfo(int compFlags, diff --git a/core/java/android/content/res/ThemedResourceCache.java b/core/java/android/content/res/ThemedResourceCache.java index 3270944ce7e3..e0701b90d6fa 100644 --- a/core/java/android/content/res/ThemedResourceCache.java +++ b/core/java/android/content/res/ThemedResourceCache.java @@ -22,10 +22,10 @@ import android.content.pm.ActivityInfo.Config; import android.content.res.Resources.Theme; import android.content.res.Resources.ThemeKey; -import android.util.ArrayMap; import android.util.LongSparseArray; import java.lang.ref.WeakReference; +import java.util.HashMap; /** * Data structure used for caching data against themes. @@ -34,7 +34,7 @@ */ abstract class ThemedResourceCache { @UnsupportedAppUsage - private ArrayMap>> mThemedEntries; + private HashMap>> mThemedEntries; private LongSparseArray> mUnthemedEntries; private LongSparseArray> mNullThemedEntries; @@ -154,7 +154,7 @@ private LongSparseArray> getThemedLocked(@Nullable Theme t, boo if (mThemedEntries == null) { if (create) { - mThemedEntries = new ArrayMap<>(1); + mThemedEntries = new HashMap<>(1); } else { return null; } @@ -199,11 +199,8 @@ private LongSparseArray> getUnthemedLocked(boolean create) { private boolean prune(@Config int configChanges) { synchronized (this) { if (mThemedEntries != null) { - for (int i = mThemedEntries.size() - 1; i >= 0; i--) { - if (pruneEntriesLocked(mThemedEntries.valueAt(i), configChanges)) { - mThemedEntries.removeAt(i); - } - } + mThemedEntries.entrySet() + .removeIf(entry -> pruneEntriesLocked(entry.getValue(), configChanges)); } pruneEntriesLocked(mNullThemedEntries, configChanges); diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java index 3bdd39f5d7d7..cbb8f3f1c96b 100644 --- a/core/java/android/hardware/Camera.java +++ b/core/java/android/hardware/Camera.java @@ -45,6 +45,7 @@ import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.SystemProperties; import android.renderscript.Allocation; import android.renderscript.Element; import android.renderscript.RSIllegalArgumentException; @@ -62,6 +63,7 @@ import java.io.IOException; import java.lang.ref.WeakReference; import java.util.ArrayList; +import java.util.Arrays; import java.util.LinkedHashMap; import java.util.List; @@ -171,6 +173,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 @@ -202,6 +208,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 @@ -264,6 +281,23 @@ public class Camera { */ private static final int CAMERA_FACE_DETECTION_SW = 1; + /** + * @hide + */ + public static boolean shouldExposeAuxCamera() { + /** + * Force to expose only two cameras + * if the package name does not falls in this bucket + */ + String packageName = ActivityThread.currentOpPackageName(); + List packageList = Arrays.asList( + SystemProperties.get("vendor.camera.aux.packagelist", packageName).split(",")); + List packageExcludelist = Arrays.asList( + SystemProperties.get("vendor.camera.aux.packageexcludelist", "").split(",")); + + return packageList.contains(packageName) && !packageExcludelist.contains(packageName); + } + /** * Returns the number of physical cameras available on this device. * The return value of this method might change dynamically if the device @@ -279,7 +313,20 @@ public class Camera { * @return total number of accessible camera devices, or 0 if there are no * cameras or an error was encountered enumerating them. */ - public native static int getNumberOfCameras(); + public static int getNumberOfCameras() { + int numberOfCameras = _getNumberOfCameras(); + if (!shouldExposeAuxCamera() && numberOfCameras > 2) { + numberOfCameras = 2; + } + return numberOfCameras; + } + + /** + * Returns the number of physical cameras available on this device. + * + * @hide + */ + public native static int _getNumberOfCameras(); /** * Returns the information about a particular camera. @@ -290,6 +337,9 @@ public class Camera { * low-level failure). */ public static void getCameraInfo(int cameraId, CameraInfo cameraInfo) { + if (cameraId >= getNumberOfCameras()) { + throw new RuntimeException("Unknown camera ID"); + } _getCameraInfo(cameraId, cameraInfo); IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE); IAudioService audioService = IAudioService.Stub.asInterface(b); @@ -323,6 +373,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. @@ -474,6 +535,10 @@ private int cameraInit(int cameraId) { 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) { @@ -490,6 +555,9 @@ private int cameraInit(int cameraId) { /** used by Camera#open, Camera#open(int) */ Camera(int cameraId) { + if (cameraId >= getNumberOfCameras()) { + throw new RuntimeException("Unknown camera ID"); + } int err = cameraInit(cameraId); if (checkInitErrors(err)) { if (err == -EACCES) { @@ -1213,7 +1281,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; @@ -1724,7 +1808,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); } @@ -2078,6 +2166,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. * @@ -2091,6 +2200,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. * @@ -2352,6 +2612,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"; @@ -2399,6 +2663,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. */ @@ -2475,6 +2744,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 @@ -2517,6 +2794,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 @@ -2569,6 +2853,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 @@ -2605,11 +2894,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 @@ -3429,8 +3722,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); @@ -4454,5 +4750,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/CameraInfo.java b/core/java/android/hardware/CameraInfo.java index 072be50ad2fb..41ef6aa54ae3 100644 --- a/core/java/android/hardware/CameraInfo.java +++ b/core/java/android/hardware/CameraInfo.java @@ -60,4 +60,4 @@ public CameraInfo[] newArray(int size) { return new CameraInfo[size]; } }; -}; +} diff --git a/core/java/android/hardware/CameraStatus.java b/core/java/android/hardware/CameraStatus.java index 874af297683e..fa35efbcee91 100644 --- a/core/java/android/hardware/CameraStatus.java +++ b/core/java/android/hardware/CameraStatus.java @@ -68,4 +68,4 @@ public CameraStatus[] newArray(int size) { return new CameraStatus[size]; } }; -}; +} diff --git a/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java b/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java index 257ad7162e9e..93b856250007 100644 --- a/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java +++ b/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java @@ -334,9 +334,6 @@ static boolean shouldDisableUdfpsDisplayMode(@FingerprintAcquired int acquiredIn case FINGERPRINT_ACQUIRED_IMMOBILE: case FINGERPRINT_ACQUIRED_TOO_BRIGHT: case FINGERPRINT_ACQUIRED_VENDOR: - // Disable the UDFPS mode because the image capture has finished. The overlay - // can be hidden later, once the authentication result arrives. - return true; case FINGERPRINT_ACQUIRED_UNKNOWN: default: // Keep the UDFPS mode in case of an unknown message. diff --git a/core/java/android/hardware/biometrics/CryptoObject.java b/core/java/android/hardware/biometrics/CryptoObject.java index d41570682fe1..267ef3637ce7 100644 --- a/core/java/android/hardware/biometrics/CryptoObject.java +++ b/core/java/android/hardware/biometrics/CryptoObject.java @@ -118,4 +118,4 @@ public final long getOpId() { } return AndroidKeyStoreProvider.getKeyStoreOperationHandle(mCrypto); } -}; +} diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java index dff2f7ed1cf3..77a6880b9e14 100644 --- a/core/java/android/hardware/camera2/CameraManager.java +++ b/core/java/android/hardware/camera2/CameraManager.java @@ -26,6 +26,7 @@ import android.content.Context; import android.content.pm.PackageManager; import android.graphics.Point; +import android.hardware.Camera; import android.hardware.CameraStatus; import android.hardware.ICameraService; import android.hardware.ICameraServiceListener; @@ -638,8 +639,14 @@ private Map getPhysicalIdToCharsMap( HashMap physicalIdsToChars = new HashMap(); Set physicalCameraIds = chars.getPhysicalCameraIds(); + CameraCharacteristics physicalChars; for (String physicalCameraId : physicalCameraIds) { - CameraCharacteristics physicalChars = getCameraCharacteristics(physicalCameraId); + try { + physicalChars = getCameraCharacteristics(physicalCameraId); + } catch (Exception e) { + physicalCameraId = "20"; + physicalChars = getCameraCharacteristics(physicalCameraId); + } physicalIdsToChars.put(physicalCameraId, physicalChars); } return physicalIdsToChars; @@ -1675,8 +1682,10 @@ private void connectCameraServiceLocked() { private String[] extractCameraIdListLocked() { String[] cameraIds = null; + boolean exposeAuxCamera = Camera.shouldExposeAuxCamera(); int idCount = 0; for (int i = 0; i < mDeviceStatus.size(); i++) { + if (!exposeAuxCamera && i == 2) break; int status = mDeviceStatus.valueAt(i); if (status == ICameraServiceListener.STATUS_NOT_PRESENT || status == ICameraServiceListener.STATUS_ENUMERATING) continue; @@ -1685,6 +1694,7 @@ private String[] extractCameraIdListLocked() { cameraIds = new String[idCount]; idCount = 0; for (int i = 0; i < mDeviceStatus.size(); i++) { + if (!exposeAuxCamera && i == 2) break; int status = mDeviceStatus.valueAt(i); if (status == ICameraServiceListener.STATUS_NOT_PRESENT || status == ICameraServiceListener.STATUS_ENUMERATING) continue; @@ -1949,6 +1959,14 @@ public void setTorchMode(String cameraId, boolean enabled) throws CameraAccessEx 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, @@ -2216,6 +2234,11 @@ private void updateCallbackLocked(AvailabilityCallback callback, Executor execut } private void onStatusChangedLocked(int status, String id) { + if (!Camera.shouldExposeAuxCamera() && Integer.parseInt(id) >= 2) { + Log.w(TAG, "[soar.cts] ignore the status update of camera: " + id); + return; + } + if (DEBUG) { Log.v(TAG, String.format("Camera id %s has status changed to 0x%x", id, status)); @@ -2347,6 +2370,15 @@ private void onTorchStatusChangedLocked(int status, String id) { String.format("Camera id %s has torch status changed to 0x%x", id, status)); } + /* 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(id) >= 2) { + Log.w(TAG, "ignore the torch status update of camera: " + id); + return; + } + if (!validTorchStatus(status)) { Log.e(TAG, String.format("Ignoring invalid device %s torch status 0x%x", id, status)); diff --git a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java index b9eba9c1d541..1c6ca081e677 100644 --- a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java @@ -15,6 +15,7 @@ */ package android.hardware.camera2.impl; +import android.app.ActivityThread; import android.hardware.camera2.CameraAccessException; import android.hardware.camera2.CameraCaptureSession; import android.hardware.camera2.CameraDevice; @@ -28,6 +29,7 @@ import android.os.Binder; import android.os.Handler; import android.os.SystemClock; +import android.os.SystemProperties; import android.util.Log; import android.view.Surface; @@ -131,6 +133,18 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession Log.e(TAG, mIdString + "Failed to create capture session; configuration failed"); mConfigureSuccess = false; } + + setSkipUnconfigure(); + } + + private void setSkipUnconfigure() { + String packageName = ActivityThread.currentOpPackageName(); + List packageList = Arrays.asList(SystemProperties.get( + "vendor.camera.skip_unconfigure.packagelist", packageName).split(",")); + + if (packageList.contains(packageName)) { + mSkipUnconfigure = true; + } } @Override @@ -222,7 +236,8 @@ private void checkCaptureRequest(CaptureRequest request) { } else if (request.isReprocess() && !isReprocessable()) { throw new IllegalArgumentException("this capture session cannot handle reprocess " + "requests"); - } else if (request.isReprocess() && request.getReprocessableSessionId() != mId) { + } else if (!mDeviceImpl.isPrivilegedApp() && + request.isReprocess() && request.getReprocessableSessionId() != mId) { throw new IllegalArgumentException("capture request was created for another session"); } } diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java index a6c79b3a289f..c7ef533cba15 100644 --- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java @@ -16,10 +16,13 @@ package android.hardware.camera2.impl; +import static android.hardware.camera2.CameraAccessException.CAMERA_IN_USE; + import static com.android.internal.util.function.pooled.PooledLambda.obtainRunnable; import android.annotation.NonNull; import android.content.Context; +import android.app.ActivityThread; import android.graphics.ImageFormat; import android.hardware.ICameraService; import android.hardware.camera2.CameraAccessException; @@ -53,6 +56,8 @@ import android.os.RemoteException; import android.os.ServiceSpecificException; import android.os.SystemClock; +import android.os.SystemProperties; +import android.text.TextUtils; import android.util.Log; import android.util.Range; import android.util.Size; @@ -151,6 +156,7 @@ public class CameraDeviceImpl extends CameraDevice private int mNextSessionId = 0; private final int mAppTargetSdkVersion; + private boolean mIsPrivilegedApp = false; private ExecutorService mOfflineSwitchService; private CameraOfflineSessionImpl mOfflineSessionImpl; @@ -301,6 +307,7 @@ public CameraDeviceImpl(String cameraId, StateCallback callback, Executor execut } else { mTotalPartialCount = partialCount; } + mIsPrivilegedApp = checkPrivilegedAppList(); } public CameraDeviceCallbacks getCallbacks() { @@ -1503,6 +1510,27 @@ private boolean checkInputConfigurationWithStreamConfigurations( return false; } + private boolean checkPrivilegedAppList() { + String packageName = ActivityThread.currentOpPackageName(); + String packageList = SystemProperties.get("persist.vendor.camera.privapp.list"); + + if (packageList.length() > 0) { + TextUtils.StringSplitter splitter = new TextUtils.SimpleStringSplitter(','); + splitter.setString(packageList); + for (String str : splitter) { + if (packageName.equals(str)) { + return true; + } + } + } + + return false; + } + + public boolean isPrivilegedApp() { + return mIsPrivilegedApp; + } + private void checkInputConfiguration(InputConfiguration inputConfig) { if (inputConfig == null) { return; @@ -1540,6 +1568,14 @@ private void checkInputConfiguration(InputConfiguration inputConfig) { inputConfig.getWidth() + "x" + inputConfig.getHeight() + " is not valid"); } } else { + /* + * don't check input format and size, + * if the package name is in the white list + */ + if (isPrivilegedApp()) { + Log.w(TAG, "ignore input format/size check for white listed app"); + return; + } if (!checkInputConfigurationWithStreamConfigurations(inputConfig, /*maxRes*/false) && !checkInputConfigurationWithStreamConfigurations(inputConfig, /*maxRes*/true)) { throw new IllegalArgumentException("Input config with format " + diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java index 468e6041eb73..80d4d6dbafab 100644 --- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java +++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java @@ -2081,6 +2081,18 @@ private static native int nativeGetTagFromKey(String keyName, long vendorId) private static native int nativeGetTypeFromTag(int tag, long vendorId) throws IllegalArgumentException; + private synchronized byte[] nativeReadValues(int tag) { + return nativeReadValues(tag, mMetadataPtr); + } + + private synchronized int nativeGetTypeFromTagLocal(int tag) { + return nativeGetTypeFromTagLocal(mMetadataPtr, tag); + } + + private synchronized int nativeGetTagFromKeyLocal(String keyname) { + return nativeGetTagFromKeyLocal(mMetadataPtr, keyname); + } + /** *

Perform a 0-copy swap of the internal metadata with another object.

* diff --git a/core/java/android/hardware/camera2/impl/FrameNumberTracker.java b/core/java/android/hardware/camera2/impl/FrameNumberTracker.java index 7b6a457411f3..8304796f636a 100644 --- a/core/java/android/hardware/camera2/impl/FrameNumberTracker.java +++ b/core/java/android/hardware/camera2/impl/FrameNumberTracker.java @@ -26,6 +26,7 @@ import java.util.Iterator; import java.util.LinkedList; import java.util.List; +import java.util.Map; import java.util.TreeMap; /** @@ -63,11 +64,11 @@ public FrameNumberTracker() { } private void update() { - Iterator iter = mFutureErrorMap.entrySet().iterator(); + Iterator> iter = mFutureErrorMap.entrySet().iterator(); while (iter.hasNext()) { - TreeMap.Entry pair = (TreeMap.Entry)iter.next(); - Long errorFrameNumber = (Long)pair.getKey(); - int requestType = (int) pair.getValue(); + Map.Entry pair = iter.next(); + long errorFrameNumber = pair.getKey(); + int requestType = pair.getValue(); Boolean removeError = false; if (errorFrameNumber == mCompletedFrameNumber[requestType] + 1) { removeError = true; diff --git a/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableEnum.java b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableEnum.java index 621a418f43c9..92a2fb6f16b1 100644 --- a/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableEnum.java +++ b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableEnum.java @@ -103,6 +103,7 @@ public Marshaler createMarshaler(TypeReference managedType, int nativeType return new MarshalerEnum(managedType, nativeType); } + @SuppressWarnings("ReturnValueIgnored") @Override public boolean isTypeMappingSupported(TypeReference managedType, int nativeType) { if (nativeType == TYPE_INT32 || nativeType == TYPE_BYTE) { diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java index 90e92dbe2ab0..558bb6335dd3 100644 --- a/core/java/android/hardware/camera2/params/OutputConfiguration.java +++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java @@ -1293,7 +1293,8 @@ public boolean equals(@Nullable Object obj) { return false; } for (int j = 0; j < mSensorPixelModesUsed.size(); j++) { - if (mSensorPixelModesUsed.get(j) != other.mSensorPixelModesUsed.get(j)) { + if (!Objects.equals( + mSensorPixelModesUsed.get(j), other.mSensorPixelModesUsed.get(j))) { return false; } } diff --git a/core/java/android/hardware/display/AmbientDisplayConfiguration.java b/core/java/android/hardware/display/AmbientDisplayConfiguration.java index 8c71b363eb7b..b52194d92239 100644 --- a/core/java/android/hardware/display/AmbientDisplayConfiguration.java +++ b/core/java/android/hardware/display/AmbientDisplayConfiguration.java @@ -142,7 +142,8 @@ && pickupGestureEnabled(user) /** @hide */ public boolean screenOffUdfpsEnabled(int user) { return !TextUtils.isEmpty(udfpsLongPressSensorType()) - && boolSettingDefaultOff("screen_off_udfps_enabled", user); + && boolSettingDefaultOff(Settings.Secure.SCREEN_OFF_UDFPS_ENABLED, user) + && mContext.getResources().getBoolean(R.bool.config_supportsScreenOffUdfps); } /** @hide */ diff --git a/core/java/android/hardware/fingerprint/Fingerprint.java b/core/java/android/hardware/fingerprint/Fingerprint.java index 9ce834ca5981..c01c94c43997 100644 --- a/core/java/android/hardware/fingerprint/Fingerprint.java +++ b/core/java/android/hardware/fingerprint/Fingerprint.java @@ -69,4 +69,4 @@ public Fingerprint[] newArray(int size) { return new Fingerprint[size]; } }; -}; \ No newline at end of file +} \ No newline at end of file diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java index 0fd164de8ffb..26c9b4c6429a 100644 --- a/core/java/android/hardware/fingerprint/FingerprintManager.java +++ b/core/java/android/hardware/fingerprint/FingerprintManager.java @@ -1051,7 +1051,7 @@ public boolean isHardwareDetected() { throw e.rethrowFromSystemServer(); } } else { - Slog.w(TAG, "isFingerprintHardwareDetected(): Service not connected!"); + if (DEBUG) Slog.w(TAG, "isFingerprintHardwareDetected(): Service not connected!"); } return false; } @@ -1456,13 +1456,15 @@ private float[] createEnrollStageThresholds(@NonNull Context context) { * @hide */ public static String getErrorString(Context context, int errMsg, int vendorCode) { + final String retry = context.getString( + com.android.internal.R.string.fingerprint_error_unable_to_process); + switch (errMsg) { case FINGERPRINT_ERROR_HW_UNAVAILABLE: return context.getString( com.android.internal.R.string.fingerprint_error_hw_not_available); case FINGERPRINT_ERROR_UNABLE_TO_PROCESS: - return context.getString( - com.android.internal.R.string.fingerprint_error_unable_to_process); + return retry; case FINGERPRINT_ERROR_TIMEOUT: return context.getString(com.android.internal.R.string.fingerprint_error_timeout); case FINGERPRINT_ERROR_NO_SPACE: @@ -1506,8 +1508,7 @@ public static String getErrorString(Context context, int errMsg, int vendorCode) // It should not happen for anything other than FINGERPRINT_ERROR_VENDOR, but // warn and use the default if all else fails. Slog.w(TAG, "Invalid error message: " + errMsg + ", " + vendorCode); - return context.getString( - com.android.internal.R.string.fingerprint_error_vendor_unknown); + return retry; } /** diff --git a/core/java/android/hardware/fingerprint/IUdfpsOverlayController.aidl b/core/java/android/hardware/fingerprint/IUdfpsOverlayController.aidl index dbb8e40f3a71..8adda2f88c8a 100644 --- a/core/java/android/hardware/fingerprint/IUdfpsOverlayController.aidl +++ b/core/java/android/hardware/fingerprint/IUdfpsOverlayController.aidl @@ -31,7 +31,7 @@ oneway interface IUdfpsOverlayController { // Check acquiredInfo for the acquired type (BiometricFingerprintConstants#FingerprintAcquired). // Check BiometricFingerprintConstants#shouldTurnOffHbm for whether the acquiredInfo // should turn off HBM. - void onAcquired(int sensorId, int acquiredInfo); + void onAcquired(int sensorId, int acquiredInfo, int vendorCode); // Notifies of enrollment progress changes. void onEnrollmentProgress(int sensorId, int remaining); diff --git a/core/java/android/hardware/usb/UsbDeviceConnection.java b/core/java/android/hardware/usb/UsbDeviceConnection.java index 60d8cacd19be..7c2e518b8544 100644 --- a/core/java/android/hardware/usb/UsbDeviceConnection.java +++ b/core/java/android/hardware/usb/UsbDeviceConnection.java @@ -107,6 +107,34 @@ boolean isOpen() { } } + /** + * This is meant to be called by UsbRequest's queue() in order to synchronize on + * UsbDeviceConnection's mLock to prevent the connection being closed while queueing. + */ + /* package */ boolean queueRequest(UsbRequest request, ByteBuffer buffer, int length) { + synchronized (mLock) { + if (!isOpen()) { + return false; + } + + return request.queueIfConnectionOpen(buffer, length); + } + } + + /** + * This is meant to be called by UsbRequest's queue() in order to synchronize on + * UsbDeviceConnection's mLock to prevent the connection being closed while queueing. + */ + /* package */ boolean queueRequest(UsbRequest request, @Nullable ByteBuffer buffer) { + synchronized (mLock) { + if (!isOpen()) { + return false; + } + + return request.queueIfConnectionOpen(buffer); + } + } + /** * Releases all system resources related to the device. * Once the object is closed it cannot be used again. diff --git a/core/java/android/hardware/usb/UsbRequest.java b/core/java/android/hardware/usb/UsbRequest.java index 6ac5e8de8fa7..beb0f8d336d5 100644 --- a/core/java/android/hardware/usb/UsbRequest.java +++ b/core/java/android/hardware/usb/UsbRequest.java @@ -113,11 +113,13 @@ public boolean initialize(UsbDeviceConnection connection, UsbEndpoint endpoint) * Releases all resources related to this request. */ public void close() { - if (mNativeContext != 0) { - mEndpoint = null; - mConnection = null; - native_close(); - mCloseGuard.close(); + synchronized (mLock) { + if (mNativeContext != 0) { + mEndpoint = null; + mConnection = null; + native_close(); + mCloseGuard.close(); + } } } @@ -191,10 +193,32 @@ public void setClientData(Object data) { */ @Deprecated public boolean queue(ByteBuffer buffer, int length) { + UsbDeviceConnection connection = mConnection; + if (connection == null) { + // The expected exception by CTS Verifier - USB Device test + throw new NullPointerException("invalid connection"); + } + + // Calling into the underlying UsbDeviceConnection to synchronize on its lock, to prevent + // the connection being closed while queueing. + return connection.queueRequest(this, buffer, length); + } + + /** + * This is meant to be called from UsbDeviceConnection after synchronizing using the lock over + * there, to prevent the connection being closed while queueing. + */ + /* package */ boolean queueIfConnectionOpen(ByteBuffer buffer, int length) { + UsbDeviceConnection connection = mConnection; + if (connection == null || !connection.isOpen()) { + // The expected exception by CTS Verifier - USB Device test + throw new NullPointerException("invalid connection"); + } + boolean out = (mEndpoint.getDirection() == UsbConstants.USB_DIR_OUT); boolean result; - if (mConnection.getContext().getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.P + if (connection.getContext().getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.P && length > MAX_USBFS_BUFFER_SIZE) { length = MAX_USBFS_BUFFER_SIZE; } @@ -243,6 +267,28 @@ public boolean queue(ByteBuffer buffer, int length) { * @return true if the queueing operation succeeded */ public boolean queue(@Nullable ByteBuffer buffer) { + UsbDeviceConnection connection = mConnection; + if (connection == null) { + // The expected exception by CTS Verifier - USB Device test + throw new IllegalStateException("invalid connection"); + } + + // Calling into the underlying UsbDeviceConnection to synchronize on its lock, to prevent + // the connection being closed while queueing. + return connection.queueRequest(this, buffer); + } + + /** + * This is meant to be called from UsbDeviceConnection after synchronizing using the lock over + * there, to prevent the connection being closed while queueing. + */ + /* package */ boolean queueIfConnectionOpen(@Nullable ByteBuffer buffer) { + UsbDeviceConnection connection = mConnection; + if (connection == null || !connection.isOpen()) { + // The expected exception by CTS Verifier - USB Device test + throw new IllegalStateException("invalid connection"); + } + // Request need to be initialized Preconditions.checkState(mNativeContext != 0, "request is not initialized"); @@ -260,7 +306,7 @@ public boolean queue(@Nullable ByteBuffer buffer) { mIsUsingNewQueue = true; wasQueued = native_queue(null, 0, 0); } else { - if (mConnection.getContext().getApplicationInfo().targetSdkVersion + if (connection.getContext().getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.P) { // Can only send/receive MAX_USBFS_BUFFER_SIZE bytes at once Preconditions.checkArgumentInRange(buffer.remaining(), 0, MAX_USBFS_BUFFER_SIZE, @@ -363,11 +409,12 @@ public boolean queue(@Nullable ByteBuffer buffer) { * @return true if cancelling succeeded */ public boolean cancel() { - if (mConnection == null) { + UsbDeviceConnection connection = mConnection; + if (connection == null) { return false; } - return mConnection.cancelRequest(this); + return connection.cancelRequest(this); } /** @@ -382,7 +429,8 @@ public boolean cancel() { * @return true if cancelling succeeded. */ /* package */ boolean cancelIfOpen() { - if (mNativeContext == 0 || (mConnection != null && !mConnection.isOpen())) { + UsbDeviceConnection connection = mConnection; + if (mNativeContext == 0 || (connection != null && !connection.isOpen())) { Log.w(TAG, "Detected attempt to cancel a request on a connection which isn't open"); return false; diff --git a/core/java/android/net/netstats/NetworkStatsDataMigrationUtils.java b/core/java/android/net/netstats/NetworkStatsDataMigrationUtils.java index 76ee097c8c93..5eeb6076b71d 100644 --- a/core/java/android/net/netstats/NetworkStatsDataMigrationUtils.java +++ b/core/java/android/net/netstats/NetworkStatsDataMigrationUtils.java @@ -53,8 +53,8 @@ import java.net.ProtocolException; import java.util.ArrayList; import java.util.Arrays; -import java.util.HashMap; import java.util.HashSet; +import java.util.Map; import java.util.Set; /** @@ -89,12 +89,10 @@ public class NetworkStatsDataMigrationUtils { @Retention(RetentionPolicy.SOURCE) public @interface Prefix {} - private static final HashMap sPrefixLegacyFileNameMap = - new HashMap() {{ - put(PREFIX_XT, "netstats_xt.bin"); - put(PREFIX_UID, "netstats_uid.bin"); - put(PREFIX_UID_TAG, "netstats_uid.bin"); - }}; + private static final Map sPrefixLegacyFileNameMap = Map.of( + PREFIX_XT, "netstats_xt.bin", + PREFIX_UID, "netstats_uid.bin", + PREFIX_UID_TAG, "netstats_uid.bin"); // These version constants are copied from NetworkStatsCollection/History, which is okay for // OEMs to modify to adapt their own logic. diff --git a/core/java/android/net/vcn/persistablebundleutils/TunnelConnectionParamsUtils.java b/core/java/android/net/vcn/persistablebundleutils/TunnelConnectionParamsUtils.java index 4bc5b49aa207..0427742f9c0a 100644 --- a/core/java/android/net/vcn/persistablebundleutils/TunnelConnectionParamsUtils.java +++ b/core/java/android/net/vcn/persistablebundleutils/TunnelConnectionParamsUtils.java @@ -53,7 +53,7 @@ public static IkeTunnelConnectionParams fromPersistableBundle(@NonNull Persistab if (in.keySet().size() != EXPECTED_BUNDLE_KEY_CNT) { throw new IllegalArgumentException( String.format( - "Expect PersistableBundle to have %d element but found: %d", + "Expect PersistableBundle to have %d element but found: %s", EXPECTED_BUNDLE_KEY_CNT, in.keySet())); } diff --git a/core/java/android/nfc/INfcAdapter.aidl b/core/java/android/nfc/INfcAdapter.aidl index cb9a3e43db81..37ba09b381ea 100644 --- a/core/java/android/nfc/INfcAdapter.aidl +++ b/core/java/android/nfc/INfcAdapter.aidl @@ -1,4 +1,7 @@ /* + * Copyright (c) 2018, The Linux Foundation. All rights reserved. + * Not a Contribution. + * * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -32,6 +35,7 @@ import android.nfc.INfcUnlockHandler; import android.nfc.ITagRemovedCallback; import android.nfc.INfcDta; import android.os.Bundle; +import android.os.IBinder; /** * @hide @@ -43,6 +47,8 @@ interface INfcAdapter INfcFCardEmulation getNfcFCardEmulationInterface(); INfcAdapterExtras getNfcAdapterExtrasInterface(in String pkg); INfcDta getNfcDtaInterface(in String pkg); + IBinder getNfcAdapterVendorInterface(in String vendor); + int getState(); boolean disable(boolean saveState); boolean enable(); diff --git a/core/java/android/nfc/cardemulation/AidGroup.java b/core/java/android/nfc/cardemulation/AidGroup.java index 2436e57b74bc..d4c966be04b8 100644 --- a/core/java/android/nfc/cardemulation/AidGroup.java +++ b/core/java/android/nfc/cardemulation/AidGroup.java @@ -1,4 +1,7 @@ /* + * Copyright (c) 2018, The Linux Foundation. All rights reserved. + * Not a Contribution. + * * Copyright (C) 2015 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -39,7 +42,7 @@ * * @hide */ -public final class AidGroup implements Parcelable { +public class AidGroup implements Parcelable { /** * The maximum number of AIDs that can be present in any one group. */ @@ -48,11 +51,11 @@ public final class AidGroup implements Parcelable { static final String TAG = "AidGroup"; @UnsupportedAppUsage - final List aids; + protected List aids; @UnsupportedAppUsage - final String category; + protected String category; @UnsupportedAppUsage - final String description; + protected String description; /** * Creates a new AidGroup object. diff --git a/core/java/android/nfc/cardemulation/ApduServiceInfo.java b/core/java/android/nfc/cardemulation/ApduServiceInfo.java index 09540132fe0d..b85fea437b24 100644 --- a/core/java/android/nfc/cardemulation/ApduServiceInfo.java +++ b/core/java/android/nfc/cardemulation/ApduServiceInfo.java @@ -1,4 +1,7 @@ /* + * Copyright (c) 2018, The Linux Foundation. All rights reserved. + * Not a Contribution. + * * Copyright (C) 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -49,24 +52,24 @@ /** * @hide */ -public final class ApduServiceInfo implements Parcelable { +public class ApduServiceInfo implements Parcelable { static final String TAG = "ApduServiceInfo"; /** * The service that implements this */ @UnsupportedAppUsage - final ResolveInfo mService; + protected ResolveInfo mService; /** * Description of the service */ - final String mDescription; + protected String mDescription; /** * Whether this service represents AIDs running on the host CPU */ - final boolean mOnHost; + protected boolean mOnHost; /** * Offhost reader name. @@ -84,18 +87,18 @@ public final class ApduServiceInfo implements Parcelable { * Mapping from category to static AID group */ @UnsupportedAppUsage - final HashMap mStaticAidGroups; + protected HashMap mStaticAidGroups; /** * Mapping from category to dynamic AID group */ @UnsupportedAppUsage - final HashMap mDynamicAidGroups; + protected HashMap mDynamicAidGroups; /** * Whether this service should only be started when the device is unlocked. */ - final boolean mRequiresDeviceUnlock; + protected boolean mRequiresDeviceUnlock; /** * Whether this service should only be started when the device is screen on. @@ -105,17 +108,17 @@ public final class ApduServiceInfo implements Parcelable { /** * The id of the service banner specified in XML. */ - final int mBannerResourceId; + protected int mBannerResourceId; /** * The uid of the package the service belongs to */ - final int mUid; + protected int mUid; /** * Settings Activity for this service */ - final String mSettingsActivityName; + protected String mSettingsActivityName; /** * @hide diff --git a/core/java/android/nfc/tech/MifareClassic.java b/core/java/android/nfc/tech/MifareClassic.java index 080e058737b6..9cae043c4bdd 100644 --- a/core/java/android/nfc/tech/MifareClassic.java +++ b/core/java/android/nfc/tech/MifareClassic.java @@ -1,4 +1,6 @@ /* + * Copyright (C) 2018 NXP Semiconductors + * The original Work has been changed by NXP Semiconductors. * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -173,6 +175,10 @@ public MifareClassic(Tag tag) throws RemoteException { mType = TYPE_CLASSIC; mSize = SIZE_4K; break; + case 0x19: + mType = TYPE_CLASSIC; + mSize = SIZE_2K; + break; case 0x28: mType = TYPE_CLASSIC; mSize = SIZE_1K; diff --git a/core/java/android/nfc/tech/NfcA.java b/core/java/android/nfc/tech/NfcA.java index 88730f9af3df..819e9e31b6e6 100644 --- a/core/java/android/nfc/tech/NfcA.java +++ b/core/java/android/nfc/tech/NfcA.java @@ -1,4 +1,6 @@ /* + * Copyright (C) 2018 NXP Semiconductors + * The original Work has been changed by NXP Semiconductors. * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -66,8 +68,15 @@ public static NfcA get(Tag tag) { /** @hide */ public NfcA(Tag tag) throws RemoteException { super(tag, TagTechnology.NFC_A); - Bundle extras = tag.getTechExtras(TagTechnology.NFC_A); - mSak = extras.getShort(EXTRA_SAK); + Bundle extras; + mSak = 0; + if(tag.hasTech(TagTechnology.MIFARE_CLASSIC)) + { + extras = tag.getTechExtras(TagTechnology.MIFARE_CLASSIC); + mSak = extras.getShort(EXTRA_SAK); + } + extras = tag.getTechExtras(TagTechnology.NFC_A); + mSak |= extras.getShort(EXTRA_SAK); mAtqa = extras.getByteArray(EXTRA_ATQA); } diff --git a/core/java/android/os/BaseBundle.java b/core/java/android/os/BaseBundle.java index b599028ccb9b..67065ae22d94 100644 --- a/core/java/android/os/BaseBundle.java +++ b/core/java/android/os/BaseBundle.java @@ -450,7 +450,14 @@ private void initializeFromParcelLocked(@NonNull Parcel parcelledData, boolean r } else { throw e; } - } finally { + } 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 { mMap = map; if (recycleParcel) { recycleParcel(parcelledData); diff --git a/core/java/android/os/BatteryManager.java b/core/java/android/os/BatteryManager.java index 76f857bd91b7..3f558183122b 100644 --- a/core/java/android/os/BatteryManager.java +++ b/core/java/android/os/BatteryManager.java @@ -163,6 +163,27 @@ 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_DASH_CHARGER = "dash_charger"; + + /** + * Extra for {@link android.content.Intent#ACTION_BATTERY_CHANGED}: + * boolean value to detect fast charging + * {@hide} + */ + public static final String EXTRA_WARP_CHARGER = "warp_charger"; + + /** + * Extra for {@link android.content.Intent#ACTION_BATTERY_CHANGED}: + * boolean value to detect fast charging + * {@hide} + */ + public static final String EXTRA_VOOC_CHARGER = "vooc_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; @@ -401,4 +422,12 @@ public boolean setChargingStateUpdateDelayMillis(int delayMillis) { throw e.rethrowFromSystemServer(); } } + + public void resetStatistics() { + try { + mBatteryStats.resetStatistics(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } } diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java index edfcb3d6f12a..0621ebc9d942 100644 --- a/core/java/android/os/FileUtils.java +++ b/core/java/android/os/FileUtils.java @@ -1299,11 +1299,13 @@ public static boolean createDir(File dir) { public static long roundStorageSize(long size) { long val = 1; long pow = 1; - while ((val * pow) < size) { + long pow1024 = 1; + while ((val * pow1024) < size) { val <<= 1; if (val > 512) { val = 1; pow *= 1000; + pow1024 *= 1024; } } return val * pow; diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl index 4fe6524dee27..cc7fb253bd95 100644 --- a/core/java/android/os/IPowerManager.aidl +++ b/core/java/android/os/IPowerManager.aidl @@ -76,6 +76,7 @@ interface IPowerManager @UnsupportedAppUsage void reboot(boolean confirm, String reason, boolean wait); void rebootSafeMode(boolean confirm, boolean wait); + void rebootCustom(boolean confirm, String reason, boolean wait); void shutdown(boolean confirm, String reason, boolean wait); void crash(String message); int getLastShutdownReason(); @@ -134,4 +135,6 @@ interface IPowerManager const int GO_TO_SLEEP_REASON_MAX = 10; const int GO_TO_SLEEP_FLAG_NO_DOZE = 1 << 0; + // Lineage custom API + void wakeUpWithProximityCheck(long time, int reason, String details, String opPackageName); } diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java index 3d701389a98e..a9be84e8f644 100644 --- a/core/java/android/os/Parcel.java +++ b/core/java/android/os/Parcel.java @@ -558,6 +558,7 @@ public final void recycle() { mPoolNext = sOwnedPool; sOwnedPool = this; sOwnedPoolSize++; + return; } } } else { @@ -567,9 +568,12 @@ public final void recycle() { mPoolNext = sHolderPool; sHolderPool = this; sHolderPoolSize++; + return; } } } + + destroy(); } /** @@ -4388,6 +4392,9 @@ public Object readLazyValue(@Nullable ClassLoader loader) { int type = readInt(); if (isLengthPrefixed(type)) { int objectLength = readInt(); + if (objectLength < 0) { + return null; + } int end = MathUtils.addOrThrow(dataPosition(), objectLength); int valueLength = end - start; setDataPosition(end); diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java index 13ca2c34b27e..9fadc42cc274 100644 --- a/core/java/android/os/PowerManager.java +++ b/core/java/android/os/PowerManager.java @@ -779,6 +779,18 @@ public int hashCode() { */ public static final String REBOOT_RECOVERY = "recovery"; + /** + * The value to pass as the 'reason' argument to reboot() to + * reboot into bootloader mode + *

+ * Requires the {@link android.Manifest.permission#RECOVERY} + * permission (in addition to + * {@link android.Manifest.permission#REBOOT}). + *

+ * @hide + */ + public static final String REBOOT_BOOTLOADER = "bootloader"; + /** * The value to pass as the 'reason' argument to reboot() to reboot into * recovery mode for applying system updates. @@ -1579,6 +1591,22 @@ public void wakeUp(long time, @WakeReason int reason, String details) { } } + /** + * Forces the device to wake up from sleep only if + * nothing is blocking the proximity sensor + * + * @see #wakeUp + * + * @hide + */ + public void wakeUpWithProximityCheck(long time, @WakeReason int reason, String details) { + try { + mService.wakeUpWithProximityCheck(time, reason, details, mContext.getOpPackageName()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + /** * Forces the device to start napping. *

@@ -1798,6 +1826,24 @@ public void rebootSafeMode() { } } + /** + * Reboot the device with custom progress meassges. + * Will not return if the reboot is successful. + *

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

+ * + * @param reason code to pass to the kernel (e.g., "recovery") to + * request special boot modes, or null. + * @hide + */ + public void rebootCustom(String reason) { + try { + mService.rebootCustom(false, reason, true); + } catch (RemoteException e) { + } + } + /** * Returns true if the device is currently in power save mode. When in this mode, * applications should reduce their functionality in order to conserve battery as diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java index e06e7326a860..411b68c89a5b 100644 --- a/core/java/android/os/Process.java +++ b/core/java/android/os/Process.java @@ -1094,6 +1094,31 @@ public static final native void setThreadGroupAndCpuset(int tid, int group) public static final native void setProcessGroup(int pid, int group) throws IllegalArgumentException, SecurityException; + /** + * Sets the scheduling group for processes in the same cgroup.procs of uid and pid + * @hide + * @param uid The user identifier of the process to change. + * @param pid The identifier of the process to change. + * @param group The target group for this process from THREAD_GROUP_*. + * @param dex2oat_only is the cgroup apply for all or for dex2oat only. + * + * @throws IllegalArgumentException Throws IllegalArgumentException if + * tid does not exist. + * @throws SecurityException Throws SecurityException if your process does + * not have permission to modify the given thread, or to use the given + * priority. + * + * group == THREAD_GROUP_DEFAULT means to move all non-background priority + * threads to the foreground scheduling group, but to leave background + * priority threads alone. group == THREAD_GROUP_BG_NONINTERACTIVE moves all + * threads, regardless of priority, to the background scheduling group. + * group == THREAD_GROUP_FOREGROUND is not allowed. + * + * Always sets cpusets. + */ + public static final native void setCgroupProcsProcessGroup(int uid, int pid, int group, boolean dex2oat_only) + throws IllegalArgumentException, SecurityException; + /** * Freeze or unfreeze the specified process. * diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java index a3b836adfc8b..2a584196ae0d 100644 --- a/core/java/android/os/RecoverySystem.java +++ b/core/java/android/os/RecoverySystem.java @@ -514,7 +514,7 @@ public static void processPackage(Context context, final Handler handler) throws IOException { String filename = packageFile.getCanonicalPath(); - if (!filename.startsWith("/data/")) { + if (!filename.startsWith("/data/") || !SystemProperties.get("persist.sys.recovery_update", "").equals("true")) { return; } @@ -629,7 +629,7 @@ public static void installPackage(Context context, File packageFile, boolean pro // If the package is on the /data partition, the package needs to // be processed (i.e. uncrypt'd). The caller specifies if that has // been done in 'processed' parameter. - if (filename.startsWith("/data/")) { + if (SystemProperties.get("persist.sys.recovery_update", "").equals("true") && filename.startsWith("/data/")) { if (processed) { if (!BLOCK_MAP_FILE.exists()) { Log.e(TAG, "Package claimed to have been processed but failed to find " @@ -857,7 +857,7 @@ public static void scheduleUpdateOnBoot(Context context, File packageFile) throw // If the package is on the /data partition, use the block map file as // the package name instead. - if (filename.startsWith("/data/")) { + if (SystemProperties.get("persist.sys.recovery_update", "").equals("true") && filename.startsWith("/data/")) { filename = "@/cache/recovery/block.map"; } diff --git a/core/java/android/os/Trace.java b/core/java/android/os/Trace.java index ac2156e9e46e..580e632b8d96 100644 --- a/core/java/android/os/Trace.java +++ b/core/java/android/os/Trace.java @@ -160,6 +160,10 @@ private Trace() { @UnsupportedAppUsage @SystemApi(client = MODULE_LIBRARIES) public static boolean isTagEnabled(long traceTag) { + if (!Build.IS_DEBUGGABLE) { + return false; + } + long tags = nativeGetEnabledTags(); return (tags & traceTag) != 0; } diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index 5487a1203833..e22cdcc0dba1 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -172,6 +172,18 @@ public class UserManager { @SystemApi public static final String USER_TYPE_SYSTEM_HEADLESS = "android.os.usertype.system.HEADLESS"; + /** + * User type representing a parallel space without the ability of sharing media. + * @hide + */ + public static final String USER_TYPE_PARALLEL_DEFAULT = "android.os.usertype.parallel.DEFAULT"; + + /** + * User type representing a parallel space with the ability of sharing media. + * @hide + */ + public static final String USER_TYPE_PARALLEL_SHARE = "android.os.usertype.parallel.SHARE"; + /** * Flag passed to {@link #requestQuietModeEnabled} to request disabling quiet mode only if * there is no need to confirm the user credentials. If credentials are required to disable @@ -4084,7 +4096,7 @@ public boolean canAddMoreUsers() { int aliveUserCount = 0; for (int i = 0; i < totalUserCount; i++) { UserInfo user = users.get(i); - if (!user.isGuest()) { + if (!user.isGuest() && !user.isParallel()) { aliveUserCount++; } } @@ -4603,10 +4615,6 @@ public boolean isQuietModeEnabled(UserHandle userHandle) { * @hide */ public boolean hasBadge(@UserIdInt int userId) { - if (!isProfile(userId)) { - // Since currently only profiles actually have badges, we can do this optimization. - return false; - } try { return mService.hasBadge(userId); } catch (RemoteException re) { diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java index 497bfa6380bc..1cf124897f05 100644 --- a/core/java/android/os/storage/StorageManager.java +++ b/core/java/android/os/storage/StorageManager.java @@ -1693,6 +1693,13 @@ public static boolean isEncrypted() { return RoSystemProperties.CRYPTO_ENCRYPTED; } + /** {@hide} + * TODO: implement a better solution for MTK devices + */ + public static boolean inCryptKeeperBounce() { + return false; + } + /** {@hide} * Is this device file encrypted? * @return true for file encrypted. (Implies isEncrypted() == true) diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java index 6b540d72bba0..a2a182cc5e56 100644 --- a/core/java/android/permission/PermissionManager.java +++ b/core/java/android/permission/PermissionManager.java @@ -217,6 +217,9 @@ public final class PermissionManager { private List mSplitPermissionInfos; + private static String[] sLocationProviderPkgNames; + private static String[] sLocationExtraPkgNames; + /** * Creates a new instance. * @@ -1165,6 +1168,16 @@ public static Set getIndicatorExemptedPackages(@NonNull Context context) pkgNames.add(exemptedPackage); } } + for (String pkgName: sLocationProviderPkgNames) { + if (pkgName != null) { + pkgNames.add(pkgName); + } + } + for (String pkgName: sLocationExtraPkgNames) { + if (pkgName != null) { + pkgNames.add(pkgName); + } + } return pkgNames; } @@ -1180,6 +1193,10 @@ public static void updateIndicatorExemptedPackages(@NonNull Context context) { for (int i = 0; i < EXEMPTED_ROLES.length; i++) { INDICATOR_EXEMPTED_PACKAGES[i] = context.getString(EXEMPTED_ROLES[i]); } + sLocationProviderPkgNames = context.getResources().getStringArray( + R.array.config_locationProviderPackageNames); + sLocationExtraPkgNames = context.getResources().getStringArray( + R.array.config_locationExtraPackageNames); } } /** diff --git a/core/java/android/pocket/IPocketCallback.aidl b/core/java/android/pocket/IPocketCallback.aidl new file mode 100644 index 000000000000..53e5412f89be --- /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 000000000000..783465774207 --- /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 000000000000..70aa74a7f2a6 --- /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 000000000000..22b60696289b --- /dev/null +++ b/core/java/android/pocket/PocketManager.java @@ -0,0 +1,233 @@ +/** + * 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.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 Context mContext; + private IPocketService mService; + private PowerManager mPowerManager; + private 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); + 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() { + 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 c6d8c08c9141..f2becb58721f 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/preference/SeekBarVolumizer.java b/core/java/android/preference/SeekBarVolumizer.java index 0a6a405fbce6..bd7831998741 100644 --- a/core/java/android/preference/SeekBarVolumizer.java +++ b/core/java/android/preference/SeekBarVolumizer.java @@ -113,6 +113,7 @@ public void onAudioVolumeGroupChanged(int group, int flags) { @UnsupportedAppUsage private final int mStreamType; private final int mMaxStreamVolume; + private final boolean mVoiceCapable; private boolean mAffectedByRingerMode; private boolean mNotificationOrRing; private final Receiver mReceiver = new Receiver(); @@ -206,6 +207,8 @@ public SeekBarVolumizer( } } mDefaultUri = defaultUri; + mVoiceCapable = context.getResources().getBoolean( + com.android.internal.R.bool.config_voice_capable); } private boolean hasAudioProductStrategies() { @@ -254,6 +257,11 @@ private static boolean isMediaStream(int stream) { return stream == AudioManager.STREAM_MUSIC; } + private boolean isNotificationStreamLinked() { + return mVoiceCapable && Settings.Secure.getInt(mContext.getContentResolver(), + Settings.Secure.VOLUME_LINK_NOTIFICATION, 1) == 1; + } + public void setSeekBar(SeekBar seekBar) { if (mSeekBar != null) { mSeekBar.setOnSeekBarChangeListener(null); @@ -281,13 +289,19 @@ protected void updateSeekBar() { mSeekBar.setProgress(mLastAudibleStreamVolume, true); } else if (mNotificationOrRing && mRingerMode == AudioManager.RINGER_MODE_VIBRATE) { mSeekBar.setProgress(0, true); + mSeekBar.setEnabled(isSeekBarEnabled()); } else if (mMuted) { mSeekBar.setProgress(0, true); } else { + mSeekBar.setEnabled(isSeekBarEnabled()); mSeekBar.setProgress(mLastProgress > -1 ? mLastProgress : mOriginalStreamVolume, true); } } + private boolean isSeekBarEnabled() { + return !(mStreamType == AudioManager.STREAM_NOTIFICATION && isNotificationStreamLinked()); + } + @Override public boolean handleMessage(Message msg) { switch (msg.what) { @@ -434,7 +448,7 @@ public void revertVolume() { } public void onProgressChanged(SeekBar seekBar, int progress, boolean fromTouch) { - if (fromTouch) { + if (fromTouch && isSeekBarEnabled()) { postSetVolume(progress); } if (mCallback != null) { @@ -631,7 +645,8 @@ public void onReceive(Context context, Intent intent) { } private void updateVolumeSlider(int streamType, int streamValue) { - final boolean streamMatch = mNotificationOrRing ? isNotificationOrRing(streamType) + final boolean streamMatch = mNotificationOrRing && isNotificationStreamLinked() + ? isNotificationOrRing(streamType) : (streamType == mStreamType); if (mSeekBar != null && streamMatch && streamValue != -1) { final boolean muted = mAudioManager.isStreamMute(mStreamType) diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java index 77c00676878c..6f1c5dbf0e1a 100644 --- a/core/java/android/provider/CallLog.java +++ b/core/java/android/provider/CallLog.java @@ -1987,7 +1987,7 @@ private static Uri addEntryAndRemoveExpiredEntries(Context context, UserManager + " WHERE " + PHONE_ACCOUNT_COMPONENT_NAME + " = ?" + " AND " + PHONE_ACCOUNT_ID + " = ?" + " ORDER BY " + DEFAULT_SORT_ORDER - + " LIMIT -1 OFFSET 500)", new String[] { + + " LIMIT -1 OFFSET 5000)", new String[] { values.getAsString(PHONE_ACCOUNT_COMPONENT_NAME), values.getAsString(PHONE_ACCOUNT_ID) }); @@ -1995,7 +1995,7 @@ private static Uri addEntryAndRemoveExpiredEntries(Context context, UserManager // No valid phone account specified, so default to the old behavior. resolver.delete(uri, "_id IN " + "(SELECT _id FROM calls ORDER BY " + DEFAULT_SORT_ORDER - + " LIMIT -1 OFFSET 500)", null); + + " LIMIT -1 OFFSET 5000)", null); } return result; diff --git a/core/java/android/provider/Downloads.java b/core/java/android/provider/Downloads.java index 0829d85801ac..c2dbfb8925ba 100644 --- a/core/java/android/provider/Downloads.java +++ b/core/java/android/provider/Downloads.java @@ -646,6 +646,11 @@ public static boolean isStatusCompleted(int status) { */ public static final int STATUS_QUEUED_FOR_WIFI = 196; + /** + * This download is paused manually. + */ + public static final int STATUS_PAUSED_MANUAL = 197; + /** * This download couldn't be completed due to insufficient storage * space. Typically, this is because the SD card is full. diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 6e369d343fa2..1b1d2998c011 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -3344,9 +3344,26 @@ public ArrayMap getStringsForPrefix(ContentResolver cr, String p } } - // Fetch all flags for the namespace at once for caching purposes - Bundle b = cp.call(cr.getAttributionSource(), - mProviderHolder.mUri.getAuthority(), mCallListCommand, null, args); + Bundle b; + // b/252663068: if we're in system server and the caller did not call + // clearCallingIdentity, the read would fail due to mismatched AttributionSources. + // TODO(b/256013480): remove this bypass after fixing the callers in system server. + if (namespace.equals(DeviceConfig.NAMESPACE_DEVICE_POLICY_MANAGER) + && Settings.isInSystemServer() + && Binder.getCallingUid() != Process.myUid()) { + final long token = Binder.clearCallingIdentity(); + try { + // Fetch all flags for the namespace at once for caching purposes + b = cp.call(cr.getAttributionSource(), + mProviderHolder.mUri.getAuthority(), mCallListCommand, null, args); + } finally { + Binder.restoreCallingIdentity(token); + } + } else { + // Fetch all flags for the namespace at once for caching purposes + b = cp.call(cr.getAttributionSource(), + mProviderHolder.mUri.getAuthority(), mCallListCommand, null, args); + } if (b == null) { // Invalid response, return an empty map return keyValues; @@ -3615,6 +3632,8 @@ public static final class System extends NameValueTable { // At one time in System, then Global, but now back in Secure MOVED_TO_SECURE.add(Secure.INSTALL_NON_MARKET_APPS); + + MOVED_TO_SECURE.add(Secure.VOLUME_LINK_NOTIFICATION); } @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @@ -4950,6 +4969,16 @@ 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} */ + public static final String RINGTONE2 = "ringtone2"; + /** * A {@link Uri} that will point to the current default ringtone at any * given time. @@ -4960,12 +4989,27 @@ 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} */ @Readable 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. * @@ -5128,6 +5172,12 @@ public static void setShowGTalkServiceStatusForUser(ContentResolver cr, boolean @Deprecated public static final String ANIMATOR_DURATION_SCALE = Global.ANIMATOR_DURATION_SCALE; + /** + * Whether or not to vibrate when a touchscreen gesture is detected + * @hide + */ + public static final String TOUCHSCREEN_GESTURE_HAPTIC_FEEDBACK = "touchscreen_gesture_haptic_feedback"; + /** * Control whether the accelerometer will be used to change screen * orientation. If 0, it will not be used unless explicitly requested @@ -5265,6 +5315,12 @@ public static void setShowGTalkServiceStatusForUser(ContentResolver cr, boolean @Readable public static final String NOTIFICATION_LIGHT_PULSE = "notification_light_pulse"; + /** + * Whether Proximity on Wake is Enabled or not + * @hide + */ + public static final String PROXIMITY_ON_WAKE = "proximity_on_wake"; + /** * Show pointer location on screen? * 0 = no @@ -5453,6 +5509,11 @@ public static void setShowGTalkServiceStatusForUser(ContentResolver cr, boolean @Readable public static final String LOCK_TO_APP_ENABLED = "lock_to_app_enabled"; + /** + * @hide + */ + public static final String GLOBAL_ACTIONS_LIST = "global_actions_list"; + /** * I am the lolrus. *

@@ -5490,6 +5551,15 @@ public static void setShowGTalkServiceStatusForUser(ContentResolver cr, boolean @Readable public static final String DESKTOP_MODE = "desktop_mode"; + /** + * Whether user can swap the order of the Alert Slider. + * * Whether user can invert the order of the Alert Slider. + * 0: Default + * 1: Inverted + * @hide + */ + public static final String ALERT_SLIDER_ORDER = "alert_slider_order"; + /** * 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 @@ -5497,6 +5567,291 @@ public static void setShowGTalkServiceStatusForUser(ContentResolver cr, boolean * the setting value. See an example above. */ + /** + * Navbar style + * @hide + */ + public static final String NAVBAR_STYLE = "navbar_style"; + + /** + * Show or hide clock + * 0 - hide + * 1 - show (default) + * @hide + */ + public static final String STATUSBAR_CLOCK = "statusbar_clock"; + + /** + * Style of clock + * 0 - Left Clock (default) + * 1 - Center Clock + * 2 - Right Clock + * @hide + */ + public static final String STATUSBAR_CLOCK_STYLE = "statusbar_clock_style"; + + /** + * Whether to show seconds next to clock in status bar + * 0 - hide (default) + * 1 - show + * @hide + */ + public static final String STATUSBAR_CLOCK_SECONDS = "statusbar_clock_seconds"; + + /** + * AM/PM Style for clock options + * 0 - Normal AM/PM + * 1 - Small AM/PM + * 2 - No AM/PM (default) + * @hide + */ + public static final String STATUSBAR_CLOCK_AM_PM_STYLE = "statusbar_clock_am_pm_style"; + + /** + * Shows custom date before clock time + * 0 - No Date + * 1 - Small Date + * 2 - Normal Date + * @hide + */ + public static final String STATUSBAR_CLOCK_DATE_DISPLAY = "statusbar_clock_date_display"; + + /** + * Sets the date string style + * 0 - Regular style + * 1 - Lowercase + * 2 - Uppercase + * @hide + */ + public static final String STATUSBAR_CLOCK_DATE_STYLE = "statusbar_clock_date_style"; + + /** + * Stores the java DateFormat string for the date + * @hide + */ + public static final String STATUSBAR_CLOCK_DATE_FORMAT = "statusbar_clock_date_format"; + + /** + * Position of date + * 0 - Left of clock + * 1 - Right of clock + * @hide + */ + public static final String STATUSBAR_CLOCK_DATE_POSITION = "statusbar_clock_date_position"; + + /** + * Defines the screen-off animation to display + * @hide + */ + public static final String SCREEN_OFF_ANIMATION = "screen_off_animation"; + + /** Volume rocker music control + * @hide + */ + public static final String VOLUME_BUTTON_MUSIC_CONTROL = "volume_button_music_control"; + + /** + * Whether to display 4G icon instead LTE + * @hide + */ + public static final String SHOW_FOURG_ICON = "show_fourg_icon"; + + /** + * @hide + */ + public static final String USE_OLD_MOBILETYPE = "use_old_mobiletype"; + + /** + * @hide + */ + public static final String SCREENSHOT_SHUTTER_SOUND = "screenshot_shutter_sound"; + + /** + * 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"; + + /** + * Whether to show heads up only for dialer and sms apps + * @hide + */ + public static final String LESS_BORING_HEADS_UP = "less_boring_heads_up"; + + /** + * Adaptive playback + * Automatically pause media when the volume is muted and + * will resume automatically when volume is restored. + * 0 = disabled + * 1 = enabled + * @hide + */ + public static final String ADAPTIVE_PLAYBACK_ENABLED = "adaptive_playback_enabled"; + + /** + * Adaptive playback's timeout in ms + * @hide + */ + public static final String ADAPTIVE_PLAYBACK_TIMEOUT = "adaptive_playback_timeout"; + + /** + + /** + * Whether the phone vibrates on call connect + * @hide + */ + public static final String VIBRATE_ON_CONNECT = "vibrate_on_connect"; + + /** + * Whether the phone vibrates on call waiting + * @hide + */ + public static final String VIBRATE_ON_CALLWAITING = "vibrate_on_callwaiting"; + + /** + * Whether the phone vibrates on disconnect + * @hide + */ + public static final String VIBRATE_ON_DISCONNECT = "vibrate_on_disconnect"; + + /** + * Double tap on lockscreen to sleep + * @hide + */ + public static final String DOUBLE_TAP_SLEEP_LOCKSCREEN = + "double_tap_sleep_lockscreen"; + + /** + * Enable statusbar double tap gesture to put device to sleep + * @hide + */ + public static final String DOUBLE_TAP_SLEEP_GESTURE = "double_tap_sleep_gesture"; + + + /** + * Whether the torch launch gesture to double tap or long press the power button when the + * screen is off should be enabled. * + * 0: disabled + * 1: long tap power for torch + * @hide + */ + public static final String TORCH_POWER_BUTTON_GESTURE = "torch_power_button_gesture"; + + /** + * Whether to hide navbar pill and keyboard space. + * Default 0. + * @hide + */ + public static final String FULLSCREEN_GESTURES = "fullscreen_gestures"; + + /** + * Whether to wake the display when plugging or unplugging the charger + * + * @hide + */ + public static final String WAKE_WHEN_PLUGGED_OR_UNPLUGGED = "wake_when_plugged_or_unplugged"; + + /** @hide */ + public static final String BACK_GESTURE_HAPTIC = "back_gesture_haptic"; + + /** + * Hide Statusbar on LockScreen + * @hide + */ + public static final String HIDE_LOCKSCREEN_STATUS_BAR = "hide_lockscreen_status_bar"; + + /** + * Wheter to show network traffic indicator in statusbar + * @hide + */ + public static final String NETWORK_TRAFFIC_STATE = "network_traffic_state"; + + /** + * Network traffic inactivity threshold + * @hide + */ + public static final String NETWORK_TRAFFIC_AUTOHIDE_THRESHOLD = "network_traffic_autohide_threshold"; + + /** + * 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"; + + /** + * reTicker Status + * @hide + */ + public static final String RETICKER_STATUS = "reticker_status"; + + /** + * reTicker Colored + * @hide + */ + public static final String RETICKER_COLORED = "reticker_colored"; + + /** + * Only enable reTicker in landscape mode + * @hide + */ + public static final String RETICKER_LANDSCAPE_ONLY = "reticker_landscape_only"; + + /** + * 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"; + + /** + * Disable power menu on secure lock screens + * + * @hide + */ + public static final String LOCK_POWER_MENU_DISABLED = "lockscreen_power_menu_disabled"; + + /** + * Whether allowing pocket service to register sensors and dispatch informations. + * 0 = disabled + * 1 = enabled + * @author Carlo Savignano + * @hide + */ + public static final String POCKET_JUDGE = "pocket_judge"; + + /** + * Bottom screen shortcuts on keyguard + * Two lists of strings delimeted by ; + * Each list of string is delimited by , + * Valid strings are: home, wallet, qr, camera, flashlight and none + * The order in each list decides the priority for each shortcut + * @hide + */ + @Readable + public static final String KEYGUARD_QUICK_TOGGLES = "keyguard_quick_toggles"; + /** * Keys we no longer back up under the current schema, but want to continue to * process when restoring historical backup datasets. @@ -5509,6 +5864,65 @@ public static void setShowGTalkServiceStatusForUser(ContentResolver cr, boolean public static final String[] LEGACY_RESTORE_SETTINGS = { }; + /** + * Three Finger Gesture from Oppo + * @hide + */ + public static final String THREE_FINGER_GESTURE = "three_finger_gesture"; + + /** + * OmniJaws weather icon pack + * @hide + */ + public static final String OMNIJAWS_WEATHER_ICON_PACK = "omnijaws_weather_icon_pack"; + + /** + * Whether to enable Smart Pixels + * @hide + */ + public static final String SMART_PIXELS_ENABLE = "smart_pixels_enable"; + + /** + * Smart Pixels pattern + * @hide + */ + public static final String SMART_PIXELS_PATTERN = "smart_pixels_pattern"; + + /** + * Smart Pixels Shift Timeout + * @hide + */ + public static final String SMART_PIXELS_SHIFT_TIMEOUT = "smart_pixels_shift_timeout"; + + /** + * Whether Smart Pixels should enable on power saver mode + * @hide + */ + public static final String SMART_PIXELS_ON_POWER_SAVE = "smart_pixels_on_power_save"; + + /** + * Enable weather in lockscreen + * @hide + */ + public static final String OMNI_LOCKSCREEN_WEATHER_ENABLED = "lockscreen_weather_enabled"; + + /** + * 0: OmniJaws Style + * 1: KeyguardSlice Style + * @hide + */ + public static final String AICP_LOCKSCREEN_WEATHER_STYLE = "lockscreen_weather_style"; + + /** + * @hide + */ + public static final String LOCKSCREEN_WEATHER_SHOW_TEMP = "lockscreen_weather_show_temp"; + + /** + * @hide + */ + public static final String LOCKSCREEN_WEATHER_SHOW_CITY = "lockscreen_weather_show_city"; + /** * These are all public system settings * @@ -5548,6 +5962,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); @@ -5569,6 +5984,12 @@ public static void setShowGTalkServiceStatusForUser(ContentResolver cr, boolean PUBLIC_SETTINGS.add(APPLY_RAMPING_RINGER); } + /** + * Whether to show the battery info on the lockscreen while charging + * @hide + */ + public static final String LOCKSCREEN_BATTERY_INFO = "lockscreen_battery_info"; + /** * These are all hidden system settings. * @@ -5620,6 +6041,30 @@ public static void setShowGTalkServiceStatusForUser(ContentResolver cr, boolean PRIVATE_SETTINGS.add(DISPLAY_COLOR_MODE); PRIVATE_SETTINGS.add(DISPLAY_COLOR_MODE_VENDOR_HINT); PRIVATE_SETTINGS.add(DESKTOP_MODE); + PRIVATE_SETTINGS.add(STATUSBAR_CLOCK); + PRIVATE_SETTINGS.add(STATUSBAR_CLOCK_STYLE); + PRIVATE_SETTINGS.add(STATUSBAR_CLOCK_SECONDS); + PRIVATE_SETTINGS.add(STATUSBAR_CLOCK_AM_PM_STYLE); + PRIVATE_SETTINGS.add(STATUSBAR_CLOCK_DATE_DISPLAY); + PRIVATE_SETTINGS.add(STATUSBAR_CLOCK_DATE_STYLE); + PRIVATE_SETTINGS.add(STATUSBAR_CLOCK_DATE_FORMAT); + PRIVATE_SETTINGS.add(STATUSBAR_CLOCK_DATE_POSITION); + PRIVATE_SETTINGS.add(VOLUME_BUTTON_MUSIC_CONTROL); + PRIVATE_SETTINGS.add(VIBRATE_ON_CONNECT); + PRIVATE_SETTINGS.add(VIBRATE_ON_CALLWAITING); + PRIVATE_SETTINGS.add(VIBRATE_ON_DISCONNECT); + PRIVATE_SETTINGS.add(DOUBLE_TAP_SLEEP_LOCKSCREEN); + PRIVATE_SETTINGS.add(DOUBLE_TAP_SLEEP_GESTURE); + PRIVATE_SETTINGS.add(BACK_GESTURE_HAPTIC); + PRIVATE_SETTINGS.add(FLASHLIGHT_ON_CALL); + PRIVATE_SETTINGS.add(FLASHLIGHT_ON_CALL_IGNORE_DND); + PRIVATE_SETTINGS.add(FLASHLIGHT_ON_CALL_RATE); + PRIVATE_SETTINGS.add(POCKET_JUDGE); + PRIVATE_SETTINGS.add(OMNIJAWS_WEATHER_ICON_PACK); + PRIVATE_SETTINGS.add(OMNI_LOCKSCREEN_WEATHER_ENABLED); + PRIVATE_SETTINGS.add(AICP_LOCKSCREEN_WEATHER_STYLE); + PRIVATE_SETTINGS.add(LOCKSCREEN_WEATHER_SHOW_TEMP); + PRIVATE_SETTINGS.add(LOCKSCREEN_WEATHER_SHOW_CITY); } /** @@ -5652,6 +6097,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); } @@ -5679,6 +6125,12 @@ public static void getCloneFromParentOnValueSettings(Map outMap) INSTANT_APP_SETTINGS.add(ACCELEROMETER_ROTATION); } + /** + * Show app volume rows in volume panel + * @hide + */ + public static final String SHOW_APP_VOLUME = "show_app_volume"; + /** * When to use Wi-Fi calling * @@ -10277,6 +10729,11 @@ public static boolean putFloatForUser(ContentResolver cr, String name, float val @Readable public static final String NOTIFICATION_DISMISS_RTL = "notification_dismiss_rtl"; + /** + * @hide + */ + public static final String ADVANCED_REBOOT = "advanced_reboot"; + /** * Comma separated list of QS tiles that have been auto-added already. * @hide @@ -10598,6 +11055,13 @@ public static boolean putFloatForUser(ContentResolver cr, String name, float val @Readable public static final String TAP_GESTURE = "tap_gesture"; + /** + * Whether to skip biometric auth confirmation + * @hide + */ + @Readable + public static final String IGNORE_AUTH_CONFIRMATION = "ignore_auth_confirmation"; + /** * Controls whether the people strip is enabled. * @hide @@ -10802,6 +11266,18 @@ public static boolean putFloatForUser(ContentResolver cr, String name, float val */ public static final String ADAPTIVE_CONNECTIVITY_ENABLED = "adaptive_connectivity_enabled"; + /** + * Boolean value whether to link ringtone and notification volume + * @hide + */ + public static final String VOLUME_LINK_NOTIFICATION = "volume_link_notification"; + + /** + * Control whether the process CPU info meter should be shown. + * @hide + */ + public static final String SHOW_CPU_OVERLAY = "show_cpu_overlay"; + /** * Keys we no longer back up under the current schema, but want to continue to * process when restoring historical backup datasets. @@ -10966,6 +11442,64 @@ public static boolean putFloatForUser(ContentResolver cr, String name, float val public static final String EXTRA_AUTOMATIC_POWER_SAVE_MODE = "extra_automatic_power_save_mode"; + /** + * Face Unlock Method + * @hide + */ + public static final String FACE_UNLOCK_METHOD = "face_unlock_method"; + + /** + * Whether to show Wi-Fi standard icon + * @hide + */ + public static final String SHOW_WIFI_STANDARD_ICON = "show_wifi_standard_icon"; + + /** + * Gesture navbar length mode. + * Supported modes: 0 for normal length, 1 for medium and 2 for long. + * Default 0. + * @hide + */ + public static final String GESTURE_NAVBAR_LENGTH_MODE = "gesture_navbar_length_mode"; + + /** + * Whether to show daily data usage in the QS footer. + * @hide + */ + public static final String QS_SHOW_DATA_USAGE = "qs_show_data_usage"; + + /** + * Control whether FLAG_SECURE is ignored for all windows. + * @hide + */ + @Readable + public static final String WINDOW_IGNORE_SECURE = "window_ignore_secure"; + + /** + * Whether to show the brightness slider in quick settings panel. + * @hide + */ + public static final String QS_SHOW_BRIGHTNESS_SLIDER = "qs_show_brightness_slider"; + + /** + * Whether to show the brightness slider in quick settings panel. + * 0 = Top, 1 = Bottom + * @hide + */ + public static final String QS_BRIGHTNESS_SLIDER_POSITION = "qs_brightness_slider_position"; + + /** + * Whether to show the auto brightness icon in quick settings panel. + * @hide + */ + public static final String QS_SHOW_AUTO_BRIGHTNESS = "qs_show_auto_brightness"; + + /** + * Control whether GMS is enabled for this user. + * @hide + */ + public static final String GMS_ENABLED = "gms_enabled"; + /** * These entries are considered common between the personal and the managed profile, * since the managed profile doesn't get to change them. @@ -11054,6 +11588,21 @@ public static boolean isLocationProviderEnabled(ContentResolver cr, String provi public static void setLocationProviderEnabled(ContentResolver cr, String provider, boolean enabled) { } + + /** + * Whether UDFPS is active while the screen is off. + * + *

1 if true, 0 or unset otherwise. + * + * @hide + */ + public static final String SCREEN_OFF_UDFPS_ENABLED = "screen_off_udfps_enabled"; + + /** + * Whether to scramble a pin unlock layout + * @hide + */ + public static final String LOCKSCREEN_PIN_SCRAMBLE_LAYOUT = "lockscreen_scramble_pin_layout"; } /** @@ -17028,6 +17577,18 @@ public static boolean putFloat(ContentResolver cr, String name, float value) { public static final String NR_NSA_TRACKING_SCREEN_OFF_MODE = "nr_nsa_tracking_screen_off_mode"; + /** + * 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"; + /** * Whether to show People Space. * Values are: @@ -18252,6 +18813,12 @@ private Panel() { @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_VOLUME = "android.settings.panel.action.VOLUME"; + + /** + * @hide + */ + public static final String ACTION_APP_VOLUME = + "android.settings.panel.action.APP_VOLUME"; } /** diff --git a/core/java/android/security/keymaster/ExportResult.java b/core/java/android/security/keymaster/ExportResult.java index 2c382efab1be..c78fb1a1f6b5 100644 --- a/core/java/android/security/keymaster/ExportResult.java +++ b/core/java/android/security/keymaster/ExportResult.java @@ -61,4 +61,4 @@ public void writeToParcel(Parcel out, int flags) { out.writeInt(resultCode); out.writeByteArray(exportData); } -}; +} diff --git a/core/java/android/service/notification/StatusBarNotification.java b/core/java/android/service/notification/StatusBarNotification.java index 66188cd19721..f8dfef59ad33 100644 --- a/core/java/android/service/notification/StatusBarNotification.java +++ b/core/java/android/service/notification/StatusBarNotification.java @@ -71,10 +71,13 @@ public class StatusBarNotification implements Parcelable { private Context mContext; // used for inflation & icon expansion + 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(); @@ -90,6 +93,7 @@ public StatusBarNotification(String pkg, String opPkg, int id, this.overrideGroupKey = overrideGroupKey; this.key = key(); this.groupKey = groupKey(); + mIsContentSecure = isContentSecure; } /** @@ -137,6 +141,7 @@ public StatusBarNotification(Parcel in) { } this.key = key(); this.groupKey = groupKey(); + mIsContentSecure = in.readBoolean(); } /** @@ -237,6 +242,7 @@ public void writeToParcel(Parcel out, int flags) { } else { out.writeInt(0); } + out.writeBoolean(mIsContentSecure); } public int describeContents() { @@ -276,7 +282,8 @@ public StatusBarNotification clone() { 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; } @@ -550,4 +557,23 @@ 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. + */ + public boolean getIsContentSecure() { + return mIsContentSecure; + } } diff --git a/core/java/android/text/style/AccessibilityURLSpan.java b/core/java/android/text/style/AccessibilityURLSpan.java index bd816234a652..e280bdf8b339 100644 --- a/core/java/android/text/style/AccessibilityURLSpan.java +++ b/core/java/android/text/style/AccessibilityURLSpan.java @@ -26,6 +26,7 @@ * It is used to replace URLSpans in {@link AccessibilityNodeInfo#setText(CharSequence)} * @hide */ +@SuppressWarnings("ParcelableCreator") public class AccessibilityURLSpan extends URLSpan implements Parcelable { final AccessibilityClickableSpan mAccessibilityClickableSpan; diff --git a/core/java/android/util/AndroidException.java b/core/java/android/util/AndroidException.java index 1345ddf189e1..d1b9d9f3c53a 100644 --- a/core/java/android/util/AndroidException.java +++ b/core/java/android/util/AndroidException.java @@ -40,5 +40,5 @@ protected AndroidException(String message, Throwable cause, boolean enableSuppre boolean writableStackTrace) { super(message, cause, enableSuppression, writableStackTrace); } -}; +} diff --git a/core/java/android/util/AndroidRuntimeException.java b/core/java/android/util/AndroidRuntimeException.java index 2b824bf9cb2a..72c34d8b75ac 100644 --- a/core/java/android/util/AndroidRuntimeException.java +++ b/core/java/android/util/AndroidRuntimeException.java @@ -34,5 +34,4 @@ public AndroidRuntimeException(String name, Throwable cause) { public AndroidRuntimeException(Exception cause) { super(cause); } -}; - +} diff --git a/core/java/android/util/BoostFramework.java b/core/java/android/util/BoostFramework.java new file mode 100644 index 000000000000..7110c1579215 --- /dev/null +++ b/core/java/android/util/BoostFramework.java @@ -0,0 +1,938 @@ +/* + * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package android.util; + +import android.app.ActivityThread; +import android.content.Context; +import android.graphics.BLASTBufferQueue; +import android.os.SystemProperties; +import android.util.Log; + +import dalvik.system.PathClassLoader; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; + +/** @hide */ +public class BoostFramework { + + private static final String TAG = "BoostFramework"; + private static final String PERFORMANCE_JAR = "/system/framework/QPerformance.jar"; + private static final String PERFORMANCE_CLASS = "com.qualcomm.qti.Performance"; + + private static final String UXPERFORMANCE_JAR = "/system/framework/UxPerformance.jar"; + private static final String UXPERFORMANCE_CLASS = "com.qualcomm.qti.UxPerformance"; + public static final float PERF_HAL_V22 = 2.2f; + public static final float PERF_HAL_V23 = 2.3f; + public static final int VENDOR_T_API_LEVEL = 33; + public final int board_first_api_lvl = SystemProperties.getInt("ro.board.first_api_level", 0); + public final int board_api_lvl = SystemProperties.getInt("ro.board.api_level", 0); + +/** @hide */ + private static boolean sIsLoaded = false; + private static Class sPerfClass = null; + private static Method sAcquireFunc = null; + private static Method sPerfHintFunc = null; + private static Method sReleaseFunc = null; + private static Method sReleaseHandlerFunc = null; + private static Method sFeedbackFunc = null; + private static Method sFeedbackFuncExtn = null; + private static Method sPerfGetPropFunc = null; + private static Method sAcqAndReleaseFunc = null; + private static Method sperfHintAcqRelFunc = null; + private static Method sperfHintRenewFunc = null; + private static Method sPerfEventFunc = null; + private static Method sPerfGetPerfHalVerFunc = null; + private static Method sPerfSyncRequest = null; + + private static Method sIOPStart = null; + private static Method sIOPStop = null; + private static Method sUXEngineEvents = null; + private static Method sUXEngineTrigger = null; + + private static boolean sUxIsLoaded = false; + private static Class sUxPerfClass = null; + private static Method sUxIOPStart = null; + + private static boolean sIsSupported = false; + +/** @hide */ + private Object mPerf = null; + private Object mUxPerf = null; + + //perf hints + public static final int VENDOR_HINT_SCROLL_BOOST = 0x00001080; + public static final int VENDOR_HINT_FIRST_LAUNCH_BOOST = 0x00001081; + public static final int VENDOR_HINT_SUBSEQ_LAUNCH_BOOST = 0x00001082; + public static final int VENDOR_HINT_ANIM_BOOST = 0x00001083; + public static final int VENDOR_HINT_ACTIVITY_BOOST = 0x00001084; + public static final int VENDOR_HINT_TOUCH_BOOST = 0x00001085; + public static final int VENDOR_HINT_MTP_BOOST = 0x00001086; + public static final int VENDOR_HINT_DRAG_BOOST = 0x00001087; + public static final int VENDOR_HINT_PACKAGE_INSTALL_BOOST = 0x00001088; + public static final int VENDOR_HINT_ROTATION_LATENCY_BOOST = 0x00001089; + public static final int VENDOR_HINT_ROTATION_ANIM_BOOST = 0x00001090; + public static final int VENDOR_HINT_PERFORMANCE_MODE = 0x00001091; + public static final int VENDOR_HINT_APP_UPDATE = 0x00001092; + public static final int VENDOR_HINT_KILL = 0x00001093; + public static final int VENDOR_HINT_BOOST_RENDERTHREAD = 0x00001096; + //perf events + public static final int VENDOR_HINT_FIRST_DRAW = 0x00001042; + public static final int VENDOR_HINT_TAP_EVENT = 0x00001043; + public static final int VENDOR_HINT_DRAG_START = 0x00001051; + public static final int VENDOR_HINT_DRAG_END = 0x00001052; + //Ime Launch Boost Hint + public static final int VENDOR_HINT_IME_LAUNCH_EVENT = 0x0000109F; + + //feedback hints + public static final int VENDOR_FEEDBACK_WORKLOAD_TYPE = 0x00001601; + public static final int VENDOR_FEEDBACK_LAUNCH_END_POINT = 0x00001602; + public static final int VENDOR_FEEDBACK_PA_FW = 0x00001604; + + //UXE Events and Triggers + public static final int UXE_TRIGGER = 1; + public static final int UXE_EVENT_BINDAPP = 2; + public static final int UXE_EVENT_DISPLAYED_ACT = 3; + public static final int UXE_EVENT_KILL = 4; + public static final int UXE_EVENT_GAME = 5; + public static final int UXE_EVENT_SUB_LAUNCH = 6; + public static final int UXE_EVENT_PKG_UNINSTALL = 7; + public static final int UXE_EVENT_PKG_INSTALL = 8; + + //New Hints while porting IOP to Perf Hal. + public static final int VENDOR_HINT_BINDAPP = 0x000010A0; + public static final int VENDOR_HINT_WARM_LAUNCH = 0x000010A1; //SUB_LAUNCH + // 0x000010A2 is added in UXPerformance.java for SPEED Hints + public static final int VENDOR_HINT_PKG_INSTALL = 0x000010A3; + public static final int VENDOR_HINT_PKG_UNINSTALL = 0x000010A4; + + //perf opcodes + public static final int MPCTLV3_GPU_IS_APP_FG = 0X42820000; + public static final int MPCTLV3_GPU_IS_APP_BG = 0X42824000; + + public class Scroll { + public static final int VERTICAL = 1; + public static final int HORIZONTAL = 2; + public static final int PANEL_VIEW = 3; + public static final int PREFILING = 4; + }; + + public class Launch { + public static final int BOOST_V1 = 1; + public static final int BOOST_V2 = 2; + public static final int BOOST_V3 = 3; + public static final int BOOST_GAME = 4; + public static final int RESERVED_1 = 5; + public static final int RESERVED_2 = 6; + public static final int RESERVED_3 = 7; + public static final int RESERVED_4 = 8; + public static final int RESERVED_5 = 9; + public static final int ACTIVITY_LAUNCH_BOOST = 10; + public static final int TYPE_SERVICE_START = 100; + public static final int TYPE_START_PROC = 101; + public static final int TYPE_START_APP_FROM_BG = 102; + public static final int TYPE_ATTACH_APPLICATION = 103; + }; + + public class Draw { + public static final int EVENT_TYPE_V1 = 1; + }; + + public class WorkloadType { + public static final int NOT_KNOWN = 0; + public static final int APP = 1; + public static final int GAME = 2; + public static final int BROWSER = 3; + public static final int PREPROAPP = 4; + }; + +/** @hide */ + public BoostFramework() { + initFunctions(ActivityThread.currentActivityThread().getSystemContext()); + + try { + if (sPerfClass != null) { + mPerf = sPerfClass.newInstance(); + } + if (sUxPerfClass != null) { + mUxPerf = sUxPerfClass.newInstance(); + } + } + catch(Exception e) { + Log.e(TAG,"BoostFramework() : Exception_2 = " + e); + } + } + +/** @hide */ + public BoostFramework(Context context) { + this(context, false); + } + +/** @hide */ + public BoostFramework(Context context, boolean isTrusted) { + initFunctions(context); + + try { + if (sPerfClass != null) { + Constructor cons = sPerfClass.getConstructor(Context.class); + if (cons != null) + mPerf = cons.newInstance(context); + } + if (sUxPerfClass != null) { + if (isTrusted) { + Constructor cons = sUxPerfClass.getConstructor(Context.class); + if (cons != null) + mUxPerf = cons.newInstance(context); + } else { + mUxPerf = sUxPerfClass.newInstance(); + } + } + } + catch(Exception e) { + Log.e(TAG,"BoostFramework() : Exception_3 = " + e); + } + } + +/** @hide */ + public BoostFramework(boolean isUntrustedDomain) { + initFunctions(ActivityThread.currentActivityThread().getSystemContext()); + + try { + if (sPerfClass != null) { + Constructor cons = sPerfClass.getConstructor(boolean.class); + if (cons != null) + mPerf = cons.newInstance(isUntrustedDomain); + } + if (sUxPerfClass != null) { + mUxPerf = sUxPerfClass.newInstance(); + } + } + catch(Exception e) { + Log.e(TAG,"BoostFramework() : Exception_5 = " + e); + } + } + + private void initFunctions (Context context) { + sIsSupported = context.getResources().getBoolean(com.android.internal.R.bool.config_supportsBoostFramework); + + synchronized(BoostFramework.class) { + if (sIsSupported && sIsLoaded == false) { + try { + sPerfClass = Class.forName(PERFORMANCE_CLASS); + + Class[] argClasses = new Class[] {int.class, int[].class}; + sAcquireFunc = sPerfClass.getMethod("perfLockAcquire", argClasses); + + argClasses = new Class[] {int.class, String.class, int.class, int.class}; + sPerfHintFunc = sPerfClass.getMethod("perfHint", argClasses); + + argClasses = new Class[] {}; + sReleaseFunc = sPerfClass.getMethod("perfLockRelease", argClasses); + + argClasses = new Class[] {int.class}; + sReleaseHandlerFunc = sPerfClass.getDeclaredMethod("perfLockReleaseHandler", argClasses); + + argClasses = new Class[] {int.class, String.class}; + sFeedbackFunc = sPerfClass.getMethod("perfGetFeedback", argClasses); + + argClasses = new Class[] {int.class, String.class, int.class, int[].class}; + sFeedbackFuncExtn = sPerfClass.getMethod("perfGetFeedbackExtn", argClasses); + + argClasses = new Class[] {int.class, String.class, String.class}; + sIOPStart = sPerfClass.getDeclaredMethod("perfIOPrefetchStart", argClasses); + + argClasses = new Class[] {}; + sIOPStop = sPerfClass.getDeclaredMethod("perfIOPrefetchStop", argClasses); + + argClasses = new Class[] {String.class, String.class}; + sPerfGetPropFunc = sPerfClass.getMethod("perfGetProp", argClasses); + + argClasses = new Class[] {int.class, int.class, int.class, int.class, int[].class}; + sAcqAndReleaseFunc = sPerfClass.getMethod("perfLockAcqAndRelease", argClasses); + + argClasses = new Class[] {int.class, String.class, int.class, int[].class}; + sPerfEventFunc = sPerfClass.getMethod("perfEvent", argClasses); + + argClasses = new Class[] {int.class}; + sPerfSyncRequest = sPerfClass.getMethod("perfSyncRequest", argClasses); + + argClasses = new Class[] {int.class, int.class, String.class, int.class, + int.class, int.class, int[].class}; + sperfHintAcqRelFunc = sPerfClass.getMethod("perfHintAcqRel", argClasses); + + argClasses = new Class[] {int.class, int.class, String.class, int.class, + int.class, int.class, int[].class}; + sperfHintRenewFunc = sPerfClass.getMethod("perfHintRenew", argClasses); + + try { + argClasses = new Class[] {}; + sPerfGetPerfHalVerFunc = sPerfClass.getMethod("perfGetHalVer", argClasses); + + } catch (Exception e) { + Log.i(TAG, "BoostFramework() : Exception_1 = perfGetHalVer not supported"); + sPerfGetPerfHalVerFunc = null; + } + + try { + argClasses = new Class[] {int.class, int.class, String.class, int.class, String.class}; + sUXEngineEvents = sPerfClass.getDeclaredMethod("perfUXEngine_events", + argClasses); + + argClasses = new Class[] {int.class}; + sUXEngineTrigger = sPerfClass.getDeclaredMethod("perfUXEngine_trigger", + argClasses); + } catch (Exception e) { + Log.i(TAG, "BoostFramework() : Exception_4 = PreferredApps not supported"); + } + + sIsLoaded = true; + } + catch(Exception e) { + Log.e(TAG,"BoostFramework() : Exception_1 = " + e); + } + // Load UXE Class now Adding new try/catch block to avoid + // any interference with Qperformance + try { + sUxPerfClass = Class.forName(UXPERFORMANCE_CLASS); + + Class[] argUxClasses = new Class[] {int.class, String.class, String.class}; + sUxIOPStart = sUxPerfClass.getDeclaredMethod("perfIOPrefetchStart", argUxClasses); + + sUxIsLoaded = true; + } + catch(Exception e) { + Log.e(TAG,"BoostFramework() Ux Perf: Exception = " + e); + } + } + } + } + +/** @hide */ + public int perfLockAcquire(int duration, int... list) { + int ret = -1; + if (!sIsSupported){ + return ret; + } + try { + if (sAcquireFunc != null) { + Object retVal = sAcquireFunc.invoke(mPerf, duration, list); + ret = (int)retVal; + } + } catch(Exception e) { + Log.e(TAG,"Exception " + e); + } + return ret; + } + +/** @hide */ + public int perfLockRelease() { + int ret = -1; + if (!sIsSupported){ + return ret; + } + try { + if (sReleaseFunc != null) { + Object retVal = sReleaseFunc.invoke(mPerf); + ret = (int)retVal; + } + } catch(Exception e) { + Log.e(TAG,"Exception " + e); + } + return ret; + } + +/** @hide */ + public int perfLockReleaseHandler(int handle) { + int ret = -1; + if (!sIsSupported){ + return ret; + } + try { + if (sReleaseHandlerFunc != null) { + Object retVal = sReleaseHandlerFunc.invoke(mPerf, handle); + ret = (int)retVal; + } + } catch(Exception e) { + Log.e(TAG,"Exception " + e); + } + return ret; + } + +/** @hide */ + public int perfHint(int hint, String userDataStr) { + return perfHint(hint, userDataStr, -1, -1); + } + +/** @hide */ + public int perfHint(int hint, String userDataStr, int userData) { + return perfHint(hint, userDataStr, userData, -1); + } + +/** @hide */ + public int perfHint(int hint, String userDataStr, int userData1, int userData2) { + int ret = -1; + if (!sIsSupported){ + return ret; + } + try { + if (sPerfHintFunc != null) { + Object retVal = sPerfHintFunc.invoke(mPerf, hint, userDataStr, userData1, userData2); + ret = (int)retVal; + } + } catch(Exception e) { + Log.e(TAG,"Exception " + e); + } + return ret; + } + +/** @hide */ + public double getPerfHalVersion() { + double retVal = PERF_HAL_V22; + if (!sIsSupported){ + return retVal; + } + try { + if (sPerfGetPerfHalVerFunc != null) { + Object ret = sPerfGetPerfHalVerFunc.invoke(mPerf); + retVal = (double)ret; + } + } catch(Exception e) { + Log.e(TAG,"Exception " + e); + } + return retVal; + } + +/** @hide */ + public int perfGetFeedback(int req, String pkg_name) { + int ret = -1; + if (!sIsSupported){ + return ret; + } + try { + if (sFeedbackFunc != null) { + Object retVal = sFeedbackFunc.invoke(mPerf, req, pkg_name); + ret = (int)retVal; + } + } catch(Exception e) { + Log.e(TAG,"Exception " + e); + } + return ret; + } + +/** @hide */ + public int perfGetFeedbackExtn(int req, String pkg_name, int numArgs, int... list) { + int ret = -1; + if (!sIsSupported){ + return ret; + } + try { + if (sFeedbackFuncExtn != null) { + Object retVal = sFeedbackFuncExtn.invoke(mPerf, req, pkg_name, numArgs, list); + ret = (int)retVal; + } + } catch(Exception e) { + Log.e(TAG,"Exception " + e); + } + return ret; + } + +/** @hide */ + public int perfIOPrefetchStart(int pid, String pkgName, String codePath) { + int ret = -1; + if (!sIsSupported){ + return ret; + } + try { + Object retVal = sIOPStart.invoke(mPerf, pid, pkgName, codePath); + ret = (int) retVal; + } catch (Exception e) { + Log.e(TAG, "Exception " + e); + } + try { + Object retVal = sUxIOPStart.invoke(mUxPerf, pid, pkgName, codePath); + ret = (int) retVal; + } catch (Exception e) { + Log.e(TAG, "Ux Perf Exception " + e); + } + + return ret; + } + +/** @hide */ + public int perfIOPrefetchStop() { + int ret = -1; + if (!sIsSupported){ + return ret; + } + try { + Object retVal = sIOPStop.invoke(mPerf); + ret = (int) retVal; + } catch (Exception e) { + Log.e(TAG, "Exception " + e); + } + return ret; + } + +/** @hide */ + public int perfUXEngine_events(int opcode, int pid, String pkgName, int lat) { + return perfUXEngine_events(opcode, pid, pkgName, lat, null); + } + +/** @hide */ + public int perfUXEngine_events(int opcode, int pid, String pkgName, int lat, String codePath) { + int ret = -1; + if (!sIsSupported){ + return ret; + } + try { + if (sUXEngineEvents == null) { + return ret; + } + + Object retVal = sUXEngineEvents.invoke(mPerf, opcode, pid, pkgName, lat,codePath); + ret = (int) retVal; + } catch (Exception e) { + Log.e(TAG, "Exception " + e); + } + return ret; + } + + +/** @hide */ + public String perfUXEngine_trigger(int opcode) { + String ret = null; + if (!sIsSupported){ + return ret; + } + try { + if (sUXEngineTrigger == null) { + return ret; + } + Object retVal = sUXEngineTrigger.invoke(mPerf, opcode); + ret = (String) retVal; + } catch (Exception e) { + Log.e(TAG, "Exception " + e); + } + return ret; + } + +/** @hide */ + public String perfSyncRequest(int opcode) { + String ret = null; + if (!sIsSupported){ + return ret; + } + try { + if (sPerfSyncRequest == null) { + return ret; + } + Object retVal = sPerfSyncRequest.invoke(mPerf, opcode); + ret = (String) retVal; + } catch (Exception e) { + Log.e(TAG, "Exception " + e); + } + return ret; + } + +/** @hide */ + public String perfGetProp(String prop_name, String def_val) { + String ret = ""; + if (!sIsSupported){ + return def_val; + } + try { + if (sPerfGetPropFunc != null) { + Object retVal = sPerfGetPropFunc.invoke(mPerf, prop_name, def_val); + ret = (String)retVal; + }else { + ret = def_val; + } + } catch(Exception e) { + Log.e(TAG,"Exception " + e); + } + return ret; + } + +/** @hide */ + public int perfLockAcqAndRelease(int handle, int duration, int numArgs,int reserveNumArgs, int... list) { + int ret = -1; + if (!sIsSupported){ + return ret; + } + try { + if (sAcqAndReleaseFunc != null) { + Object retVal = sAcqAndReleaseFunc.invoke(mPerf, handle, duration, numArgs, reserveNumArgs, list); + ret = (int)retVal; + } + } catch(Exception e) { + Log.e(TAG,"Exception " + e); + } + return ret; + } + +/** @hide */ + public void perfEvent(int eventId, String pkg_name) { + perfEvent(eventId, pkg_name, 0); + } + +/** @hide */ + public void perfEvent(int eventId, String pkg_name, int numArgs, int... list) { + if (!sIsSupported){ + return; + } + try { + if (sPerfEventFunc != null) { + sPerfEventFunc.invoke(mPerf, eventId, pkg_name, numArgs, list); + } + } catch(Exception e) { + Log.e(TAG,"Exception " + e); + } + } + +/** @hide */ + public int perfHintAcqRel(int handle, int hint, String pkg_name) { + return perfHintAcqRel(handle, hint, pkg_name, -1, -1, 0); + } + +/** @hide */ + public int perfHintAcqRel(int handle, int hint, String pkg_name, int duration) { + return perfHintAcqRel(handle, hint, pkg_name, duration, -1, 0); + } + +/** @hide */ + public int perfHintAcqRel(int handle, int hint, String pkg_name, int duration, int hintType) { + return perfHintAcqRel(handle, hint, pkg_name, duration, hintType, 0); + } + +/** @hide */ + public int perfHintAcqRel(int handle, int hint, String pkg_name, int duration, + int hintType, int numArgs, int... list) { + int ret = -1; + if (!sIsSupported){ + return ret; + } + try { + if (sperfHintAcqRelFunc != null) { + Object retVal = sperfHintAcqRelFunc.invoke(mPerf,handle, hint, pkg_name, + duration, hintType, numArgs, list); + ret = (int)retVal; + } + } catch(Exception e) { + Log.e(TAG,"Exception " + e); + } + return ret; + } + +/** @hide */ + public int perfHintRenew(int handle, int hint, String pkg_name) { + return perfHintRenew(handle, hint, pkg_name, -1, -1, 0); + } + +/** @hide */ + public int perfHintRenew(int handle, int hint, String pkg_name, int duration) { + return perfHintRenew(handle, hint, pkg_name, duration, -1, 0); + } + +/** @hide */ + public int perfHintRenew(int handle, int hint, String pkg_name, int duration, int hintType) { + return perfHintRenew(handle, hint, pkg_name, duration, hintType, 0); + } + +/** @hide */ + public int perfHintRenew(int handle, int hint, String pkg_name, int duration, + int hintType, int numArgs, int... list) { + int ret = -1; + if (!sIsSupported){ + return ret; + } + try { + if (sperfHintRenewFunc != null) { + Object retVal = sperfHintRenewFunc.invoke(mPerf,handle, hint, pkg_name, + duration, hintType, numArgs, list); + ret = (int)retVal; + } + } catch(Exception e) { + Log.e(TAG,"Exception " + e); + } + return ret; + } + + /** @hide */ + public static class ScrollOptimizer { + /** @hide */ + public static final int FLING_START = 1; + /** @hide */ + public static final int FLING_END = 0; + private static final String SCROLL_OPT_PROP = "ro.vendor.perf.scroll_opt"; + private static final String QXPERFORMANCE_JAR = + "/system/framework/QXPerformance.jar"; + private static final String SCROLL_OPT_CLASS = + "com.qualcomm.qti.QXPerformance.ScrollOptimizer"; + private static boolean sScrollOptProp = false; + private static boolean sScrollOptEnable = false; + private static boolean sQXIsLoaded = false; + private static Class sQXPerfClass = null; + private static Method sSetFrameInterval = null; + private static Method sDisableOptimizer = null; + private static Method sSetBLASTBufferQueue = null; + private static Method sSetMotionType = null; + private static Method sSetVsyncTime = null; + private static Method sSetUITaskStatus = null; + private static Method sSetFlingFlag = null; + private static Method sShouldUseVsync = null; + private static Method sGetFrameDelay = null; + private static Method sGetAdjustedAnimationClock = null; + + private static void initQXPerfFuncs() { + if (!sIsSupported || sQXIsLoaded) return; + + try { + sScrollOptProp = SystemProperties.getBoolean(SCROLL_OPT_PROP, false); + if (!sScrollOptProp) { + sScrollOptEnable = false; + sQXIsLoaded = true; + return; + } + + PathClassLoader qXPerfClassLoader = new PathClassLoader( + QXPERFORMANCE_JAR, ClassLoader.getSystemClassLoader()); + sQXPerfClass = qXPerfClassLoader.loadClass(SCROLL_OPT_CLASS); + Class[] argClasses = new Class[]{long.class}; + sSetFrameInterval = sQXPerfClass.getMethod( + "setFrameInterval", argClasses); + + argClasses = new Class[]{boolean.class}; + sDisableOptimizer = sQXPerfClass.getMethod("disableOptimizer", argClasses); + + argClasses = new Class[]{BLASTBufferQueue.class}; + sSetBLASTBufferQueue = sQXPerfClass.getMethod("setBLASTBufferQueue", argClasses); + + argClasses = new Class[]{int.class}; + sSetMotionType = sQXPerfClass.getMethod("setMotionType", argClasses); + + argClasses = new Class[]{long.class}; + sSetVsyncTime = sQXPerfClass.getMethod("setVsyncTime", argClasses); + + argClasses = new Class[]{boolean.class}; + sSetUITaskStatus = sQXPerfClass.getMethod("setUITaskStatus", argClasses); + + argClasses = new Class[]{int.class}; + sSetFlingFlag = sQXPerfClass.getMethod("setFlingFlag", argClasses); + + sShouldUseVsync = sQXPerfClass.getMethod("shouldUseVsync"); + + argClasses = new Class[]{long.class}; + sGetFrameDelay = sQXPerfClass.getMethod("getFrameDelay", argClasses); + + argClasses = new Class[]{long.class}; + sGetAdjustedAnimationClock = sQXPerfClass.getMethod( + "getAdjustedAnimationClock", argClasses); + } catch (Exception e) { + Log.e(TAG, "initQXPerfFuncs failed"); + e.printStackTrace(); + } finally { + // If frameworks and perf changes don't match(may not built together) + // or other exception, need to set sQXIsLoaded as true to avoid retry. + sQXIsLoaded = true; + } + } + + /** @hide */ + public static void setFrameInterval(long frameIntervalNanos) { + if (!sIsSupported){ + return; + } + if (sQXIsLoaded) { + if (sScrollOptEnable && sSetFrameInterval != null) { + try { + sSetFrameInterval.invoke(null, frameIntervalNanos); + } catch (Exception e) { + e.printStackTrace(); + } + } + return; + } + Thread initThread = new Thread(new Runnable() { + @Override + public void run() { + synchronized(ScrollOptimizer.class) { + try { + initQXPerfFuncs(); + if (sScrollOptProp && sSetFrameInterval != null) { + sSetFrameInterval.invoke(null, frameIntervalNanos); + sScrollOptEnable = true; + } + } catch (Exception e) { + Log.e(TAG, "Failed to run initThread."); + e.printStackTrace(); + } + } + } + }); + initThread.start(); + } + + /** @hide */ + public static void disableOptimizer(boolean disabled) { + if (!sIsSupported){ + return; + } + if (sScrollOptEnable && sDisableOptimizer != null) { + try { + sDisableOptimizer.invoke(null, disabled); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + /** @hide */ + public static void setBLASTBufferQueue(BLASTBufferQueue blastBufferQueue) { + if (!sIsSupported){ + return; + } + if (sScrollOptEnable && sSetBLASTBufferQueue != null) { + try { + sSetBLASTBufferQueue.invoke(null, blastBufferQueue); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + /** @hide */ + public static void setMotionType(int eventType) { + if (!sIsSupported){ + return; + } + if (sScrollOptEnable && sSetMotionType != null) { + try { + sSetMotionType.invoke(null, eventType); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + /** @hide */ + public static void setVsyncTime(long vsyncTimeNanos) { + if (!sIsSupported){ + return; + } + if (sScrollOptEnable && sSetVsyncTime != null) { + try { + sSetVsyncTime.invoke(null, vsyncTimeNanos); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + /** @hide */ + public static void setUITaskStatus(boolean running) { + if (!sIsSupported){ + return; + } + if (sScrollOptEnable && sSetUITaskStatus != null) { + try { + sSetUITaskStatus.invoke(null, running); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + /** @hide */ + public static void setFlingFlag(int flag) { + if (!sIsSupported){ + return; + } + if (sScrollOptEnable && sSetFlingFlag != null) { + try { + sSetFlingFlag.invoke(null, flag); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + /** @hide */ + public static boolean shouldUseVsync(boolean defaultVsyncFlag) { + boolean useVsync = defaultVsyncFlag; + if (!sIsSupported){ + return useVsync; + } + if (sScrollOptEnable && sShouldUseVsync != null) { + try { + Object retVal = sShouldUseVsync.invoke(null); + useVsync = (boolean)retVal; + } catch (Exception e) { + e.printStackTrace(); + } + } + return useVsync; + } + + /** @hide */ + public static long getFrameDelay(long defaultDelay, long lastFrameTimeNanos) { + long frameDelay = defaultDelay; + if (!sIsSupported){ + return frameDelay; + } + if (sScrollOptEnable && sGetFrameDelay != null) { + try { + Object retVal = sGetFrameDelay.invoke(null, lastFrameTimeNanos); + frameDelay = (long)retVal; + } catch (Exception e) { + e.printStackTrace(); + } + } + return frameDelay; + } + + /** @hide */ + public static long getAdjustedAnimationClock(long frameTimeNanos) { + long newFrameTimeNanos = frameTimeNanos; + if (!sIsSupported){ + return newFrameTimeNanos; + } + if (sScrollOptEnable && sGetAdjustedAnimationClock != null) { + try { + Object retVal = sGetAdjustedAnimationClock.invoke(null, + frameTimeNanos); + newFrameTimeNanos = (long)retVal; + } catch (Exception e) { + e.printStackTrace(); + } + } + return newFrameTimeNanos; + } + } +}; diff --git a/core/java/android/util/DisplayUtils.java b/core/java/android/util/DisplayUtils.java index cbb38a4ada31..91562c5d3f5d 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/EventLog.java b/core/java/android/util/EventLog.java index 4654dbfa9531..a09bc6977660 100644 --- a/core/java/android/util/EventLog.java +++ b/core/java/android/util/EventLog.java @@ -328,15 +328,19 @@ public int hashCode() { } } - // We assume that the native methods deal with any concurrency issues. - /** * Record an event log message. * @param tag The event type tag code * @param value A value to log * @return The number of bytes written */ - public static native int writeEvent(int tag, int value); + public static int writeEvent(int tag, int value) { + if (!Build.IS_DEBUGGABLE) { + return 0; + } + + return nativeWriteEvent(tag, value); + } /** * Record an event log message. @@ -344,7 +348,13 @@ public int hashCode() { * @param value A value to log * @return The number of bytes written */ - public static native int writeEvent(int tag, long value); + public static int writeEvent(int tag, long value) { + if (!Build.IS_DEBUGGABLE) { + return 0; + } + + return nativeWriteEvent(tag, value); + } /** * Record an event log message. @@ -352,7 +362,13 @@ public int hashCode() { * @param value A value to log * @return The number of bytes written */ - public static native int writeEvent(int tag, float value); + public static int writeEvent(int tag, float value) { + if (!Build.IS_DEBUGGABLE) { + return 0; + } + + return nativeWriteEvent(tag, value); + } /** * Record an event log message. @@ -360,7 +376,13 @@ public int hashCode() { * @param str A value to log * @return The number of bytes written */ - public static native int writeEvent(int tag, String str); + public static int writeEvent(int tag, String str) { + if (!Build.IS_DEBUGGABLE) { + return 0; + } + + return nativeWriteEvent(tag, str); + } /** * Record an event log message. @@ -368,7 +390,13 @@ public int hashCode() { * @param list A list of values to log * @return The number of bytes written */ - public static native int writeEvent(int tag, Object... list); + public static int writeEvent(int tag, Object... list) { + if (!Build.IS_DEBUGGABLE) { + return 0; + } + + return nativeWriteEvent(tag, list); + } /** * Read events from the log, filtered by type. @@ -376,8 +404,14 @@ public int hashCode() { * @param output container to add events into * @throws IOException if something goes wrong reading events */ - public static native void readEvents(int[] tags, Collection output) - throws IOException; + public static void readEvents(int[] tags, Collection output) + throws IOException { + if (!Build.IS_DEBUGGABLE) { + return; + } + + nativeReadEvents(tags, output); + } /** * Read events from the log, filtered by type, blocking until logs are about to be overwritten. @@ -388,7 +422,27 @@ public static native void readEvents(int[] tags, Collection output) * @hide */ @SystemApi - public static native void readEventsOnWrapping(int[] tags, long timestamp, + public static void readEventsOnWrapping(int[] tags, long timestamp, + Collection output) + throws IOException { + if (!Build.IS_DEBUGGABLE) { + return; + } + + nativeReadEventsOnWrapping(tags, timestamp, output); + } + + // We assume that the native methods deal with any concurrency issues. + + private static native int nativeWriteEvent(int tag, int value); + private static native int nativeWriteEvent(int tag, long value); + private static native int nativeWriteEvent(int tag, float value); + private static native int nativeWriteEvent(int tag, String str); + private static native int nativeWriteEvent(int tag, Object... list); + + private static native void nativeReadEvents(int[] tags, Collection output) + throws IOException; + private static native void nativeReadEventsOnWrapping(int[] tags, long timestamp, Collection output) throws IOException; diff --git a/core/java/android/util/Range.java b/core/java/android/util/Range.java index 9fd0ab99f01b..41c171a0bbd7 100644 --- a/core/java/android/util/Range.java +++ b/core/java/android/util/Range.java @@ -356,4 +356,4 @@ public int hashCode() { private final T mLower; private final T mUpper; -}; +} diff --git a/core/java/android/util/TypedValue.java b/core/java/android/util/TypedValue.java index 19de396c4a4a..44318bbc5468 100644 --- a/core/java/android/util/TypedValue.java +++ b/core/java/android/util/TypedValue.java @@ -696,5 +696,5 @@ public String toString() sb.append("}"); return sb.toString(); } -}; +} diff --git a/core/java/android/util/apk/ApkSignatureVerifier.java b/core/java/android/util/apk/ApkSignatureVerifier.java index d2a18dd84313..da04acedfc8d 100644 --- a/core/java/android/util/apk/ApkSignatureVerifier.java +++ b/core/java/android/util/apk/ApkSignatureVerifier.java @@ -33,8 +33,11 @@ import android.os.Build; import android.os.Trace; import android.os.incremental.V4Signature; +import android.util.ArrayMap; import android.util.Pair; +import android.util.Slog; import android.util.jar.StrictJarFile; +import android.util.BoostFramework; import com.android.internal.util.ArrayUtils; @@ -53,6 +56,9 @@ import java.util.Map; import java.util.concurrent.atomic.AtomicReference; import java.util.zip.ZipEntry; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.LinkedBlockingQueue; /** * Facade class that takes care of the details of APK verification on @@ -64,6 +70,12 @@ public class ApkSignatureVerifier { private static final AtomicReference sBuffer = new AtomicReference<>(); + private static final String TAG = "ApkSignatureVerifier"; + // multithread verification + private static final int NUMBER_OF_CORES = + Runtime.getRuntime().availableProcessors() >= 4 ? 4 : Runtime.getRuntime().availableProcessors() ; + private static BoostFramework sPerfBoost = null; + private static boolean sIsPerfLockAcquired = false; /** * Verifies the provided APK and returns the certificates associated with each signer. */ @@ -101,6 +113,7 @@ private static ParseResult verifySignatures(ParseInput input, St * Verifies the provided APK using all allowed signing schemas. * @return the certificates associated with each signer and content digests. * @param verifyFull whether to verify all contents of this APK or just collect certificates. + * @throws PackageParserException if there was a problem collecting certificates * @hide */ public static ParseResult verifySignaturesInternal(ParseInput input, @@ -361,32 +374,45 @@ private static ParseResult verifyV2Signature(ParseInp */ private static ParseResult verifyV1Signature(ParseInput input, String apkPath, boolean verifyFull) { - StrictJarFile jarFile = null; - + int objectNumber = verifyFull ? NUMBER_OF_CORES : 1; + StrictJarFile[] jarFile = new StrictJarFile[objectNumber]; + final ArrayMap strictJarFiles = new ArrayMap(); try { final Certificate[][] lastCerts; final Signature[] lastSigs; Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "strictJarFileCtor"); + if (sPerfBoost == null) { + sPerfBoost = new BoostFramework(); + } + if (sPerfBoost != null && !sIsPerfLockAcquired && verifyFull) { + //Use big enough number here to hold the perflock for entire PackageInstall session + sPerfBoost.perfHint(BoostFramework.VENDOR_HINT_PACKAGE_INSTALL_BOOST, + null, Integer.MAX_VALUE, -1); + Slog.d(TAG, "Perflock acquired for PackageInstall "); + sIsPerfLockAcquired = true; + } // we still pass verify = true to ctor to collect certs, even though we're not checking // the whole jar. - jarFile = new StrictJarFile( - apkPath, - true, // collect certs - verifyFull); // whether to reject APK with stripped v2 signatures (b/27887819) + for (int i = 0; i < objectNumber; i++) { + jarFile[i] = new StrictJarFile( + apkPath, + true, // collect certs + verifyFull); // whether to reject APK with stripped v2 signatures (b/27887819) + } final List toVerify = new ArrayList<>(); // Gather certs from AndroidManifest.xml, which every APK must have, as an optimization // to not need to verify the whole APK when verifyFUll == false. - final ZipEntry manifestEntry = jarFile.findEntry( + final ZipEntry manifestEntry = jarFile[0].findEntry( ApkLiteParseUtils.ANDROID_MANIFEST_FILENAME); if (manifestEntry == null) { return input.error(INSTALL_PARSE_FAILED_BAD_MANIFEST, "Package " + apkPath + " has no manifest"); } final ParseResult result = - loadCertificates(input, jarFile, manifestEntry); + loadCertificates(input, jarFile[0], manifestEntry); if (result.isError()) { return input.error(result); } @@ -400,7 +426,7 @@ private static ParseResult verifyV1Signature(ParseInp // fully verify all contents, except for AndroidManifest.xml and the META-INF/ files. if (verifyFull) { - final Iterator i = jarFile.iterator(); + final Iterator i = jarFile[0].iterator(); while (i.hasNext()) { final ZipEntry entry = i.next(); if (entry.isDirectory()) continue; @@ -411,30 +437,101 @@ private static ParseResult verifyV1Signature(ParseInp toVerify.add(entry); } - + class VerificationData { + public Exception exception; + public int exceptionFlag; + public boolean wait; + public int index; + public Object objWaitAll; + public boolean shutDown; + } + VerificationData vData = new VerificationData(); + vData.objWaitAll = new Object(); + final ThreadPoolExecutor verificationExecutor = new ThreadPoolExecutor( + NUMBER_OF_CORES, + NUMBER_OF_CORES, + 1,/*keep alive time*/ + TimeUnit.SECONDS, + new LinkedBlockingQueue()); for (ZipEntry entry : toVerify) { - final Certificate[][] entryCerts; - final ParseResult ret = - loadCertificates(input, jarFile, entry); - if (ret.isError()) { - return input.error(ret); - } - entryCerts = ret.getResult(); - if (ArrayUtils.isEmpty(entryCerts)) { - return input.error(INSTALL_PARSE_FAILED_NO_CERTIFICATES, - "Package " + apkPath + " has no certificates at entry " - + entry.getName()); + Runnable verifyTask = new Runnable(){ + public void run() { + try { + if (vData.exceptionFlag != 0 ) { + Slog.w(TAG, "VerifyV1 exit with exception " + vData.exceptionFlag); + return; + } + String tid = Long.toString(Thread.currentThread().getId()); + StrictJarFile tempJarFile; + synchronized (strictJarFiles) { + tempJarFile = strictJarFiles.get(tid); + if (tempJarFile == null) { + if (vData.index >= NUMBER_OF_CORES) { + vData.index = 0; + } + tempJarFile = jarFile[vData.index++]; + strictJarFiles.put(tid, tempJarFile); + } + } + final Certificate[][] entryCerts; + final ParseResult ret = + loadCertificates(input, tempJarFile, entry); + if (ret.isError()) { + throw new SecurityException(ret.getException()); + } + entryCerts = ret.getResult(); + if (ArrayUtils.isEmpty(entryCerts)) { + throw new SignatureNotFoundException("Package " + apkPath + " has no certificates at entry " + + entry.getName()); + } + + // make sure all entries use the same signing certs + final Signature[] entrySigs = convertToSignatures(entryCerts); + if (!Signature.areExactMatch(lastSigs, entrySigs)) { + throw new Exception("Package " + apkPath + " has mismatched certificates at entry " + + entry.getName()); + } + } catch (SignatureNotFoundException | SecurityException e) { + synchronized (vData.objWaitAll) { + vData.exceptionFlag = INSTALL_PARSE_FAILED_NO_CERTIFICATES; + vData.exception = e; + } + } catch (GeneralSecurityException e) { + synchronized (vData.objWaitAll) { + vData.exceptionFlag = INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING; + vData.exception = e; + } + } catch (Exception e) { + synchronized (vData.objWaitAll) { + vData.exceptionFlag = INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES; + vData.exception = e; + } + } + }}; + synchronized (vData.objWaitAll) { + if (vData.exceptionFlag == 0) { + verificationExecutor.execute(verifyTask); + } } - - // make sure all entries use the same signing certs - final Signature[] entrySigs = convertToSignatures(entryCerts); - if (!Signature.areExactMatch(lastSigs, entrySigs)) { - return input.error( - INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES, - "Package " + apkPath + " has mismatched certificates at entry " - + entry.getName()); + } + vData.wait = true; + verificationExecutor.shutdown(); + while (vData.wait) { + try { + if (vData.exceptionFlag != 0 && !vData.shutDown) { + Slog.w(TAG, "verifyV1 Exception " + vData.exceptionFlag); + verificationExecutor.shutdownNow(); + vData.shutDown = true; + } + vData.wait = !verificationExecutor.awaitTermination(50, + TimeUnit.MILLISECONDS); + } catch (InterruptedException e) { + Slog.w(TAG,"VerifyV1 interrupted while awaiting all threads done..."); } } + if (vData.exceptionFlag != 0) + return input.error(vData.exceptionFlag, + "Failed to collect certificates from " + apkPath, vData.exception); } return input.success(new SigningDetailsWithDigests( new SigningDetails(lastSigs, SignatureSchemeVersion.JAR), null)); @@ -445,8 +542,16 @@ private static ParseResult verifyV1Signature(ParseInp return input.error(INSTALL_PARSE_FAILED_NO_CERTIFICATES, "Failed to collect certificates from " + apkPath, e); } finally { + if (sIsPerfLockAcquired && sPerfBoost != null) { + sPerfBoost.perfLockRelease(); + sIsPerfLockAcquired = false; + Slog.d(TAG, "Perflock released for PackageInstall "); + } + strictJarFiles.clear(); Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); - closeQuietly(jarFile); + for (int i = 0; i < objectNumber ; i++) { + closeQuietly(jarFile[i]); + } } } @@ -512,9 +617,6 @@ private static void closeQuietly(StrictJarFile jarFile) { * {@code targetSdk}. */ public static int getMinimumSignatureSchemeVersionForTargetSdk(int targetSdk) { - if (targetSdk >= Build.VERSION_CODES.R) { - return SignatureSchemeVersion.SIGNING_BLOCK_V2; - } return SignatureSchemeVersion.JAR; } diff --git a/core/java/android/view/CutoutSpecification.java b/core/java/android/view/CutoutSpecification.java index f8aa934af595..3fc3b6a3ccb3 100644 --- a/core/java/android/view/CutoutSpecification.java +++ b/core/java/android/view/CutoutSpecification.java @@ -394,7 +394,6 @@ private void parseSvgPathSpec(Region region, String spec) { Log.e(TAG, "According to SVG definition, it shouldn't happen"); return; } - spec.trim(); translateMatrix(); final Path newPath = PathParser.createPathFromPathData(spec); diff --git a/core/java/android/view/LayoutInflater.java b/core/java/android/view/LayoutInflater.java index df78827534a6..cd2ea4311264 100644 --- a/core/java/android/view/LayoutInflater.java +++ b/core/java/android/view/LayoutInflater.java @@ -804,67 +804,75 @@ 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 = tryCreateViewDirect(prefixedName, viewContext, attrs); + 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) @@ -1363,4 +1371,121 @@ 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); + case "android.widget.QuickContactBadge": // 50 occurrences + return new android.widget.QuickContactBadge(context, attributeSet); + case "android.widget.SeekBar": // 40 occurrences + return new android.widget.SeekBar(context, attributeSet); + case "android.widget.CheckBox": // 38 occurrences + return new android.widget.CheckBox(context, attributeSet); + case "android.widget.GridLayout": // 16 occurrences + return new android.widget.GridLayout(context, attributeSet); + case "android.widget.TableRow": // 15 occurrences + return new android.widget.TableRow(context, attributeSet); + case "android.widget.RadioGroup": // 15 occurrences + return new android.widget.RadioGroup(context, attributeSet); + case "android.widget.Chronometer": // 15 occurrences + return new android.widget.Chronometer(context, attributeSet); + case "android.widget.ViewFlipper": // 13 occurrences + return new android.widget.ViewFlipper(context, attributeSet); + case "android.widget.Spinner": // 9 occurrences + return new android.widget.Spinner(context, attributeSet); + case "android.widget.ViewSwitcher": // 8 occurrences + return new android.widget.ViewSwitcher(context, attributeSet); + case "android.widget.TextSwitcher": // 8 occurrences + return new android.widget.TextSwitcher(context, attributeSet); + case "android.widget.SurfaceView": // 8 occurrences + case "android.webkit.SurfaceView": // 1 occurrence + case "android.view.SurfaceView": // 1 occurrence + case "android.app.SurfaceView": // 1 occurrence + return new android.view.SurfaceView(context, attributeSet); + case "android.widget.CheckedTextView": // 8 occurrences + return new android.widget.CheckedTextView(context, attributeSet); + case "android.preference.PreferenceFrameLayout": // 8 occurrences + return new android.preference.PreferenceFrameLayout(context, attributeSet); + case "android.widget.TwoLineListItem": // 7 occurrences + return new android.widget.TwoLineListItem(context, attributeSet); + case "android.widget.TableLayout": // 5 occurrences + return new android.widget.TableLayout(context, attributeSet); + case "android.widget.EditText": // 5 occurrences + return new android.widget.EditText(context, attributeSet); + case "android.widget.TabWidget": // 3 occurrences + return new android.widget.TabWidget(context, attributeSet); + case "android.widget.TabHost": // 3 occurrences + return new android.widget.TabHost(context, attributeSet); + case "android.widget.ZoomButton": // 2 occurrences + return new android.widget.ZoomButton(context, attributeSet); + case "android.widget.TextureView": // 2 occurrences + case "android.webkit.TextureView": // 2 occurrences + case "android.app.TextureView": // 2 occurrences + case "android.view.TextureView": // 2 occurrences + return new android.view.TextureView(context, attributeSet); + case "android.widget.ExpandableListView": // 2 occurrences + return new android.widget.ExpandableListView(context, attributeSet); + case "android.widget.ViewAnimator": // 1 occurrence + return new android.widget.ViewAnimator(context, attributeSet); + case "android.widget.TextClock": // 1 occurrence + return new android.widget.TextClock(context, attributeSet); + case "android.widget.AutoCompleteTextView": // 1 occurrence + return new android.widget.AutoCompleteTextView(context, attributeSet); + case "android.widget.WebView": // 1 occurrence + case "android.webkit.WebView": // 1 occurrence + return new android.webkit.WebView(context, attributeSet); + } + + return null; + } } diff --git a/core/java/android/view/RemoteAnimationDefinition.java b/core/java/android/view/RemoteAnimationDefinition.java index ea9799584e20..ff282ba0da39 100644 --- a/core/java/android/view/RemoteAnimationDefinition.java +++ b/core/java/android/view/RemoteAnimationDefinition.java @@ -19,6 +19,7 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import android.annotation.Nullable; +import android.annotation.NonNull; import android.app.WindowConfiguration.ActivityType; import android.compat.annotation.UnsupportedAppUsage; import android.os.IBinder; @@ -157,7 +158,7 @@ public void writeToParcel(Parcel dest, int flags) { } } - public static final @android.annotation.NonNull Creator CREATOR = + public static final @NonNull Creator CREATOR = new Creator() { public RemoteAnimationDefinition createFromParcel(Parcel in) { return new RemoteAnimationDefinition(in); @@ -199,18 +200,17 @@ public int describeContents() { return 0; } - private static final @android.annotation.NonNull Creator CREATOR - = new Creator() { - - @Override - public RemoteAnimationAdapterEntry createFromParcel(Parcel in) { - return new RemoteAnimationAdapterEntry(in); - } - - @Override - public RemoteAnimationAdapterEntry[] newArray(int size) { - return new RemoteAnimationAdapterEntry[size]; - } - }; + public static final @NonNull Parcelable.Creator CREATOR = + new Parcelable.Creator() { + @Override + public RemoteAnimationAdapterEntry createFromParcel(Parcel in) { + return new RemoteAnimationAdapterEntry(in); + } + + @Override + public RemoteAnimationAdapterEntry[] newArray(int size) { + return new RemoteAnimationAdapterEntry[size]; + } + }; } } diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java index 638b8f9f9b40..6d6692c64a5d 100644 --- a/core/java/android/view/ViewConfiguration.java +++ b/core/java/android/view/ViewConfiguration.java @@ -74,7 +74,7 @@ public class ViewConfiguration { * a long press * @hide */ - public static final int DEFAULT_LONG_PRESS_TIMEOUT = 400; + public static final int DEFAULT_LONG_PRESS_TIMEOUT = 300; /** * Defines the default duration in milliseconds between the first tap's up event and the second @@ -92,7 +92,7 @@ public class ViewConfiguration { * appropriate button to bring up the global actions dialog (power off, * lock screen, etc). */ - private static final int GLOBAL_ACTIONS_KEY_TIMEOUT = 500; + private static final int GLOBAL_ACTIONS_KEY_TIMEOUT = 250; /** * Defines the duration in milliseconds a user needs to hold down the @@ -124,7 +124,7 @@ public class ViewConfiguration { * is a jump tap. If the user does not complete the jump tap within this interval, it is * considered to be a tap. */ - private static final int JUMP_TAP_TIMEOUT = 500; + private static final int JUMP_TAP_TIMEOUT = 250; /** * Defines the duration in milliseconds between the first tap's up event and @@ -158,12 +158,12 @@ public class ViewConfiguration { * Defines the duration in milliseconds we want to display zoom controls in response * to a user panning within an application. */ - private static final int ZOOM_CONTROLS_TIMEOUT = 3000; + private static final int ZOOM_CONTROLS_TIMEOUT = 1500; /** * Inset in dips to look for touchable content when the user touches the edge of the screen */ - private static final int EDGE_SLOP = 12; + private static final int EDGE_SLOP = 6; /** * Distance a touch can wander before we think the user is scrolling in dips. @@ -222,7 +222,7 @@ public class ViewConfiguration { /** * Maximum velocity to initiate a fling, as measured in dips per second */ - private static final int MAXIMUM_FLING_VELOCITY = 8000; + private static final int MAXIMUM_FLING_VELOCITY = 16000; /** * Delay before dispatching a recurring accessibility event in milliseconds. @@ -243,7 +243,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.012f; /** * Max distance in dips to overscroll for edge effects diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index e4caa385bc9b..2006d8265f96 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -6649,6 +6649,11 @@ private int processPointerEvent(QueuedInputEvent q) { final MotionEvent event = (MotionEvent)q.mEvent; mHandwritingInitiator.onTouchEvent(event); + if (event.getPointerCount() == 3 && isSwipeToScreenshotGestureActive()) { + event.setAction(MotionEvent.ACTION_CANCEL); + Log.d("teste", "canceling motionEvent because of threeGesture detecting"); + } + mAttachInfo.mUnbufferedDispatchRequested = false; mAttachInfo.mHandlingPointerEvent = true; boolean handled = mView.dispatchPointerEvent(event); @@ -11114,4 +11119,13 @@ void mergeSync(int syncId, SurfaceSyncer otherSyncer) { } mSurfaceSyncer.merge(mSyncId, syncId, otherSyncer); } + + private boolean isSwipeToScreenshotGestureActive() { + try { + return ActivityManager.getService().isSwipeToScreenshotGestureActive(); + } catch (RemoteException e) { + Log.e("teste", "isSwipeToScreenshotGestureActive exception", e); + return false; + } + } } diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java index 02027e4a3969..22e605bdc3ad 100644 --- a/core/java/android/view/Window.java +++ b/core/java/android/view/Window.java @@ -20,6 +20,7 @@ import static android.Manifest.permission.HIDE_OVERLAY_WINDOWS; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.view.WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; +import static android.view.WindowManager.LayoutParams.FLAG_SECURE; import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS; import android.annotation.ColorInt; @@ -50,6 +51,7 @@ import android.os.Bundle; import android.os.Handler; import android.os.IBinder; +import android.provider.Settings; import android.transition.Scene; import android.transition.Transition; import android.transition.TransitionManager; @@ -1274,6 +1276,10 @@ public void clearFlags(int flags) { * @see #clearFlags */ public void setFlags(int flags, int mask) { + if ((mask & FLAG_SECURE) != 0 && Settings.Secure.getInt(mContext.getContentResolver(), + Settings.Secure.WINDOW_IGNORE_SECURE, 0) == 1) { + mask &= ~FLAG_SECURE; + } final WindowManager.LayoutParams attrs = getAttributes(); attrs.flags = (attrs.flags&~mask) | (flags&mask); mForcedWindowFlags |= mask; diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java index d37756551db3..ae9a1220a246 100644 --- a/core/java/android/view/WindowManagerGlobal.java +++ b/core/java/android/view/WindowManagerGlobal.java @@ -32,6 +32,7 @@ import android.os.SystemProperties; import android.util.AndroidRuntimeException; import android.util.ArraySet; +import android.util.BoostFramework.ScrollOptimizer; import android.util.Log; import android.view.inputmethod.InputMethodManager; @@ -362,10 +363,12 @@ public void addView(View view, ViewGroup.LayoutParams params, // The previous removeView() had not completed executing. Now it has. } + boolean isSubWindow = false; // If this is a panel window, then find the window it is being // attached to for future reference. if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW && wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) { + isSubWindow = true; final int count = mViews.size(); for (int i = 0; i < count; i++) { if (mRoots.get(i).mWindow.asBinder() == wparams.token) { @@ -396,6 +399,19 @@ public void addView(View view, ViewGroup.LayoutParams params, view.setLayoutParams(wparams); + int visibleRootCount = 0; + if (!isSubWindow) { + for (int i = mRoots.size() - 1; i >= 0; --i) { + View root_view = mRoots.get(i).getView(); + if (root_view != null && root_view.getVisibility() == View.VISIBLE) { + visibleRootCount++; + } + } + } + if (isSubWindow || visibleRootCount > 1) { + ScrollOptimizer.disableOptimizer(true); + } + mViews.add(view); mRoots.add(root); mParams.add(wparams); @@ -522,6 +538,18 @@ void doRemoveView(ViewRootImpl root) { final View view = mViews.remove(index); mDyingViews.remove(view); } + + int visibleRootCount = 0; + for (int i = mRoots.size() - 1; i >= 0; --i) { + View root_view = mRoots.get(i).getView(); + if (root_view != null && root_view.getVisibility() == View.VISIBLE) { + visibleRootCount++; + } + } + if (visibleRootCount == 1) { + ScrollOptimizer.disableOptimizer(false); + } + allViewsRemoved = mRoots.isEmpty(); } if (ThreadedRenderer.sTrimForeground) { diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index b5a742bfb2eb..ef4a77a04fd8 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -73,6 +73,7 @@ import android.os.UserHandle; import android.provider.Settings; import android.text.style.SuggestionSpan; +import android.util.BoostFramework; import android.util.Log; import android.util.Pools.Pool; import android.util.Pools.SimplePool; @@ -282,6 +283,11 @@ public final class InputMethodManager { */ private static final String SUBTYPE_MODE_VOICE = "voice"; + //Perf + static BoostFramework mPerfBoost = null; + static boolean IME_BOOST_ENABLED = false; + static boolean isImeBoostPropertyRead = false; + /** * Provide this to {@link IInputMethodManager#startInputOrWindowGainedFocus( * int, IInputMethodClient, IBinder, int, int, int, EditorInfo, IInputContext, int)} to receive @@ -657,6 +663,20 @@ public boolean startInput(@StartInputReason int startInputReason, View focusedVi ImeTracing.getInstance().triggerClientDump( "InputMethodManager.DelegateImpl#startInput", InputMethodManager.this, null /* icProto */); + + if (isImeBoostPropertyRead == false) { + mPerfBoost = new BoostFramework(); + + if (mPerfBoost != null) { + IME_BOOST_ENABLED = Boolean.parseBoolean(mPerfBoost.perfGetProp("ro.vendor.qti.sys.fw.use_ime_boost", "false")); + } + isImeBoostPropertyRead = true; + } + + if (IME_BOOST_ENABLED == true && mPerfBoost != null) { + mPerfBoost.perfEvent(BoostFramework.VENDOR_HINT_IME_LAUNCH_EVENT, null); + } + synchronized (mH) { mCurrentTextBoxAttribute = null; mCompletions = null; diff --git a/core/java/android/webkit/ConsoleMessage.java b/core/java/android/webkit/ConsoleMessage.java index 5474557c9998..89cb6b2761be 100644 --- a/core/java/android/webkit/ConsoleMessage.java +++ b/core/java/android/webkit/ConsoleMessage.java @@ -68,4 +68,4 @@ public String sourceId() { public int lineNumber() { return mLineNumber; } -}; +} diff --git a/core/java/android/webkit/ValueCallback.java b/core/java/android/webkit/ValueCallback.java index 5c7d97fc5d8c..3d5bb4922a77 100644 --- a/core/java/android/webkit/ValueCallback.java +++ b/core/java/android/webkit/ValueCallback.java @@ -25,4 +25,4 @@ public interface ValueCallback { * @param value The value. */ public void onReceiveValue(T value); -}; +} diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index 0b0bfb1ddbe9..20333f72e6f9 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -684,6 +684,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]; @@ -994,6 +995,7 @@ private void initAbsListView() { mVerticalScrollFactor = configuration.getScaledVerticalScrollFactor(); mMinimumVelocity = configuration.getScaledMinimumFlingVelocity(); mMaximumVelocity = configuration.getScaledMaximumFlingVelocity(); + mDecacheThreshold = mMaximumVelocity / 2; mOverscrollDistance = configuration.getScaledOverscrollDistance(); mOverflingDistance = configuration.getScaledOverflingDistance(); @@ -4811,7 +4813,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); } @@ -4827,6 +4829,11 @@ public void run() { // 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); @@ -4907,6 +4914,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); @@ -4915,7 +4926,8 @@ void endFling() { if (!mSuppressIdleStateChangeCall) { reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE); } - clearScrollingCache(); + if (clearCache) + clearScrollingCache(); mScroller.abortAnimation(); if (mFlingStrictSpan != null) { diff --git a/core/java/android/widget/AbsSeekBar.java b/core/java/android/widget/AbsSeekBar.java index 6281ee9d05d1..3946fb621720 100644 --- a/core/java/android/widget/AbsSeekBar.java +++ b/core/java/android/widget/AbsSeekBar.java @@ -1013,7 +1013,15 @@ private void trackTouchEvent(MotionEvent event) { progress += scale * range + getMin(); setHotspot(x, y); - setProgressInternal(Math.round(progress), true, false); + setProgressInternal(updateTouchProgress(getProgress(), + Math.round(progress)), true, false); + } + + /** + * @hide + */ + protected int updateTouchProgress(int lastProgress, int newProgress) { + return newProgress; } /** diff --git a/core/java/android/widget/OverScroller.java b/core/java/android/widget/OverScroller.java index 1683878cd8b2..93dbfa07fc4c 100644 --- a/core/java/android/widget/OverScroller.java +++ b/core/java/android/widget/OverScroller.java @@ -20,6 +20,7 @@ import android.content.Context; import android.hardware.SensorManager; import android.os.Build; +import android.util.BoostFramework.ScrollOptimizer; import android.util.Log; import android.view.ViewConfiguration; import android.view.animation.AnimationUtils; @@ -361,6 +362,7 @@ public void startScroll(int startX, int startY, int dx, int dy) { * @param duration Duration of the scroll in milliseconds. */ public void startScroll(int startX, int startY, int dx, int dy, int duration) { + ScrollOptimizer.setFlingFlag(ScrollOptimizer.FLING_END); mMode = SCROLL_MODE; mScrollerX.startScroll(startX, dx, duration); mScrollerY.startScroll(startY, dy, duration); diff --git a/core/java/android/window/TransitionFilter.java b/core/java/android/window/TransitionFilter.java index db15145b0a1e..e62d5c95a1f8 100644 --- a/core/java/android/window/TransitionFilter.java +++ b/core/java/android/window/TransitionFilter.java @@ -296,7 +296,7 @@ public String toString() { out.append((i == 0 ? "" : ",") + TransitionInfo.modeToString(mModes[i])); } } - out.append("]").toString(); + out.append("]"); out.append(" flags=" + TransitionInfo.flagsToString(mFlags)); out.append(" mustBeTask=" + mMustBeTask); out.append(" order=" + containerOrderToString(mOrder)); diff --git a/core/java/com/android/internal/app/ActivityTrigger.java b/core/java/com/android/internal/app/ActivityTrigger.java new file mode 100644 index 000000000000..dbcb13f49ef5 --- /dev/null +++ b/core/java/com/android/internal/app/ActivityTrigger.java @@ -0,0 +1,101 @@ +/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +package com.android.internal.app; + +import android.content.ComponentName; +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; +import android.util.Log; + +public class ActivityTrigger +{ + private static final String TAG = "ActivityTrigger"; + + /** @hide */ + public ActivityTrigger() { + //Log.d(TAG, "ActivityTrigger initialized"); + } + + /** @hide */ + protected void finalize() { + native_at_deinit(); + } + + /** @hide */ + public void activityStartTrigger(ApplicationInfo appInfo, int pid) { + int reserved =0; + String activity = null; + activity = appInfo.packageName + "/" + appInfo.processName + "/" + + appInfo.longVersionCode + "/" + pid; + native_at_startApp(activity, reserved); + } + + /** @hide */ + public void activityResumeTrigger(Intent intent, ActivityInfo acInfo, + ApplicationInfo appInfo, boolean IsInFullScreen) { + ComponentName cn = intent.getComponent(); + String activity = null; + + if (cn != null) + activity = cn.flattenToString() + "/" + appInfo.versionCode; + native_at_resumeActivity(activity); + } + + public void activityPauseTrigger(Intent intent, ActivityInfo acInfo, ApplicationInfo appInfo) { + ComponentName cn = intent.getComponent(); + String activity = null; + Log.d(TAG, "ActivityTrigger activityPauseTrigger "); + if (null != cn && null != appInfo) + activity = cn.flattenToString() + "/" + appInfo.versionCode; + native_at_pauseActivity(activity); + } + + public void activityStopTrigger(Intent intent, ActivityInfo acInfo, ApplicationInfo appInfo) { + ComponentName cn = intent.getComponent(); + String activity = null; + Log.d(TAG, "ActivityTrigger activityStopTrigger "); + if (null != cn && null != appInfo) + activity = cn.flattenToString() + "/" + appInfo.versionCode; + native_at_stopActivity(activity); + } + + public float activityMiscTrigger(int func, String activity, int flag, int type) { + return native_at_miscActivity(func, activity, flag, type); + } + + private native int native_at_startActivity(String activity, int flags); + private native int native_at_startApp(String activity, int flags); + private native void native_at_resumeActivity(String activity); + private native void native_at_pauseActivity(String activity); + private native void native_at_stopActivity(String activity); + private native void native_at_deinit(); + private native float native_at_miscActivity(int func, String activity, int flag, int type); +} diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl index 30da4b470ab6..88447daf7338 100644 --- a/core/java/com/android/internal/app/IAppOpsService.aidl +++ b/core/java/com/android/internal/app/IAppOpsService.aidl @@ -58,11 +58,12 @@ interface IAppOpsService { SyncNotedAppOp noteProxyOperation(int code, in AttributionSource attributionSource, boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage, boolean skipProxyOperation); - SyncNotedAppOp startProxyOperation(int code, in AttributionSource attributionSource, - boolean startIfModeDefault, boolean shouldCollectAsyncNotedOp, String message, - boolean shouldCollectMessage, boolean skipProxyOperation, int proxyAttributionFlags, - int proxiedAttributionFlags, int attributionChainId); - void finishProxyOperation(int code, in AttributionSource attributionSource, + SyncNotedAppOp startProxyOperation(IBinder clientId, int code, + in AttributionSource attributionSource, boolean startIfModeDefault, + boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage, + boolean skipProxyOperation, int proxyAttributionFlags, int proxiedAttributionFlags, + int attributionChainId); + void finishProxyOperation(IBinder clientId, int code, in AttributionSource attributionSource, boolean skipProxyOperation); // Remaining methods are only used in Java. diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl index 629a1b36b9e6..c1fa18f04922 100644 --- a/core/java/com/android/internal/app/IBatteryStats.aidl +++ b/core/java/com/android/internal/app/IBatteryStats.aidl @@ -189,4 +189,7 @@ interface IBatteryStats { void resetBattery(boolean forceUpdate); /** Exposed as a test API. */ void suspendBatteryInput(); + + /** {@hide} */ + void resetStatistics(); } diff --git a/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java b/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java index 5f4a9cd5141e..473134ea46f3 100644 --- a/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java +++ b/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java @@ -172,14 +172,14 @@ public boolean start(Activity activity, Bundle options) { @Override public boolean startAsCaller(ResolverActivity activity, Bundle options, int userId) { - prepareIntentForCrossProfileLaunch(mResolvedIntent, userId); + TargetInfo.prepareIntentForCrossProfileLaunch(mResolvedIntent, userId); activity.startActivityAsCaller(mResolvedIntent, options, false, userId); return true; } @Override public boolean startAsUser(Activity activity, Bundle options, UserHandle user) { - prepareIntentForCrossProfileLaunch(mResolvedIntent, user.getIdentifier()); + TargetInfo.prepareIntentForCrossProfileLaunch(mResolvedIntent, user.getIdentifier()); activity.startActivityAsUser(mResolvedIntent, options, user); return false; } @@ -224,13 +224,6 @@ public DisplayResolveInfo[] newArray(int size) { } }; - private static void prepareIntentForCrossProfileLaunch(Intent intent, int targetUserId) { - final int currentUserId = UserHandle.myUserId(); - if (targetUserId != currentUserId) { - intent.fixUris(currentUserId); - } - } - private DisplayResolveInfo(Parcel in) { mDisplayLabel = in.readCharSequence(); mExtendedInfo = in.readCharSequence(); diff --git a/core/java/com/android/internal/app/chooser/SelectableTargetInfo.java b/core/java/com/android/internal/app/chooser/SelectableTargetInfo.java index 264e4f76d35d..4b9b7cb98dac 100644 --- a/core/java/com/android/internal/app/chooser/SelectableTargetInfo.java +++ b/core/java/com/android/internal/app/chooser/SelectableTargetInfo.java @@ -232,6 +232,7 @@ public boolean startAsCaller(ResolverActivity activity, Bundle options, int user } intent.setComponent(mChooserTarget.getComponentName()); intent.putExtras(mChooserTarget.getIntentExtras()); + TargetInfo.prepareIntentForCrossProfileLaunch(intent, userId); // Important: we will ignore the target security checks in ActivityManager // if and only if the ChooserTarget's target package is the same package diff --git a/core/java/com/android/internal/app/chooser/TargetInfo.java b/core/java/com/android/internal/app/chooser/TargetInfo.java index f56ab17cb059..7bb7ddc65c6d 100644 --- a/core/java/com/android/internal/app/chooser/TargetInfo.java +++ b/core/java/com/android/internal/app/chooser/TargetInfo.java @@ -130,4 +130,15 @@ public interface TargetInfo { * @return true if this target should be pinned to the front by the request of the user */ boolean isPinned(); + + /** + * Fix the URIs in {@code intent} if cross-profile sharing is required. This should be called + * before launching the intent as another user. + */ + static void prepareIntentForCrossProfileLaunch(Intent intent, int targetUserId) { + final int currentUserId = UserHandle.myUserId(); + if (targetUserId != currentUserId) { + intent.fixUris(currentUserId); + } + } } diff --git a/core/java/com/android/internal/app/procstats/AssociationState.java b/core/java/com/android/internal/app/procstats/AssociationState.java index 97f4b0fc8733..a21a84261ae0 100644 --- a/core/java/com/android/internal/app/procstats/AssociationState.java +++ b/core/java/com/android/internal/app/procstats/AssociationState.java @@ -59,6 +59,7 @@ public final class AssociationState { /** * The state of the source process of an association. */ + @SuppressWarnings("ParcelableCreator") public static final class SourceState implements Parcelable { private @NonNull final ProcessStats mProcessStats; private @Nullable final AssociationState mAssociationState; diff --git a/core/java/com/android/internal/bootleggers/hardware/HIDLHelper.java b/core/java/com/android/internal/bootleggers/hardware/HIDLHelper.java new file mode 100644 index 000000000000..d67274feb8da --- /dev/null +++ b/core/java/com/android/internal/bootleggers/hardware/HIDLHelper.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2019 The LineageOS 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.bootleggers.hardware; + +import android.util.Range; + +import java.util.ArrayList; + +class HIDLHelper { + + static TouchscreenGesture[] fromHIDLGestures( + ArrayList gestures) { + int size = gestures.size(); + TouchscreenGesture[] r = new TouchscreenGesture[size]; + for (int i = 0; i < size; i++) { + vendor.lineage.touch.V1_0.Gesture g = gestures.get(i); + r[i] = new TouchscreenGesture(g.id, g.name, g.keycode); + } + return r; + } + + static vendor.lineage.touch.V1_0.Gesture toHIDLGesture(TouchscreenGesture gesture) { + vendor.lineage.touch.V1_0.Gesture g = new vendor.lineage.touch.V1_0.Gesture(); + g.id = gesture.id; + g.name = gesture.name; + g.keycode = gesture.keycode; + return g; + } + +} diff --git a/core/java/com/android/internal/bootleggers/hardware/LineageHardwareManager.java b/core/java/com/android/internal/bootleggers/hardware/LineageHardwareManager.java new file mode 100644 index 000000000000..a88e23b5107f --- /dev/null +++ b/core/java/com/android/internal/bootleggers/hardware/LineageHardwareManager.java @@ -0,0 +1,259 @@ +/* + * Copyright (C) 2015-2016 The CyanogenMod Project + * 2017-2019 The LineageOS 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.bootleggers.hardware; + +import android.content.Context; +import android.hidl.base.V1_0.IBase; +import android.os.IBinder; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.util.ArrayMap; +import android.util.Log; +import android.util.Range; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.ArrayUtils; + +import com.android.internal.bootleggers.hardware.HIDLHelper; + +import vendor.lineage.touch.V1_0.IGloveMode; +import vendor.lineage.touch.V1_0.IKeyDisabler; +import vendor.lineage.touch.V1_0.IStylusMode; +import vendor.lineage.touch.V1_0.ITouchscreenGesture; + +import java.io.UnsupportedEncodingException; +import java.lang.IllegalArgumentException; +import java.lang.reflect.Field; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.NoSuchElementException; + +/** + * Manages access to LineageOS hardware extensions + * + *

+ * This manager requires the HARDWARE_ABSTRACTION_ACCESS permission. + *

+ * To get the instance of this class, utilize LineageHardwareManager#getInstance(Context context) + */ +public final class LineageHardwareManager { + private static final String TAG = "LineageHardwareManager"; + + // The VisibleForTesting annotation is to ensure Proguard doesn't remove these + // fields, as they might be used via reflection. When the @Keep annotation in + // the support library is properly handled in the platform, we should change this. + + /** + * High touch sensitivity for touch panels + */ + @VisibleForTesting + public static final int FEATURE_HIGH_TOUCH_SENSITIVITY = 0x10; + + /** + * Hardware navigation key disablement + */ + @VisibleForTesting + public static final int FEATURE_KEY_DISABLE = 0x20; + + /** + * Touchscreen hovering + */ + @VisibleForTesting + public static final int FEATURE_TOUCH_HOVERING = 0x800; + + /** + * Touchscreen gesture + */ + @VisibleForTesting + public static final int FEATURE_TOUCHSCREEN_GESTURES = 0x80000; + + private static final List BOOLEAN_FEATURES = Arrays.asList( + FEATURE_HIGH_TOUCH_SENSITIVITY, + FEATURE_KEY_DISABLE, + FEATURE_TOUCH_HOVERING + ); + + private static LineageHardwareManager sLineageHardwareManagerInstance; + + private Context mContext; + + // HIDL hals + private HashMap mHIDLMap = new HashMap(); + + /** + * @hide to prevent subclassing from outside of the framework + */ + private LineageHardwareManager(Context context) { + Context appContext = context.getApplicationContext(); + if (appContext != null) { + mContext = appContext; + } else { + mContext = context; + } + } + + /** + * Determine if a Lineage Hardware feature is supported on this device + * + * @param feature The Lineage Hardware feature to query + * + * @return true if the feature is supported, false otherwise. + */ + public boolean isSupported(int feature) { + return isSupportedHIDL(feature); + } + + private boolean isSupportedHIDL(int feature) { + if (!mHIDLMap.containsKey(feature)) { + mHIDLMap.put(feature, getHIDLService(feature)); + } + return mHIDLMap.get(feature) != null; + } + + private IBase getHIDLService(int feature) { + try { + switch (feature) { + case FEATURE_HIGH_TOUCH_SENSITIVITY: + return IGloveMode.getService(true); + case FEATURE_KEY_DISABLE: + return IKeyDisabler.getService(true); + case FEATURE_TOUCH_HOVERING: + return IStylusMode.getService(true); + case FEATURE_TOUCHSCREEN_GESTURES: + return ITouchscreenGesture.getService(true); + } + } catch (NoSuchElementException | RemoteException e) { + } + return null; + } + + /** + * Get or create an instance of the {@link com.android.internal.custom.hardware.LineageHardwareManager} + * @param context + * @return {@link LineageHardwareManager} + */ + public static LineageHardwareManager getInstance(Context context) { + if (sLineageHardwareManagerInstance == null) { + sLineageHardwareManagerInstance = new LineageHardwareManager(context); + } + return sLineageHardwareManagerInstance; + } + + /** + * Determine if the given feature is enabled or disabled. + * + * Only used for features which have simple enable/disable controls. + * + * @param feature the Lineage Hardware feature to query + * + * @return true if the feature is enabled, false otherwise. + */ + public boolean get(int feature) { + if (!BOOLEAN_FEATURES.contains(feature)) { + throw new IllegalArgumentException(feature + " is not a boolean"); + } + + try { + if (isSupportedHIDL(feature)) { + IBase obj = mHIDLMap.get(feature); + switch (feature) { + case FEATURE_HIGH_TOUCH_SENSITIVITY: + IGloveMode gloveMode = (IGloveMode) obj; + return gloveMode.isEnabled(); + case FEATURE_KEY_DISABLE: + IKeyDisabler keyDisabler = (IKeyDisabler) obj; + return keyDisabler.isEnabled(); + case FEATURE_TOUCH_HOVERING: + IStylusMode stylusMode = (IStylusMode) obj; + return stylusMode.isEnabled(); + } + } + } catch (RemoteException e) { + } + return false; + } + + /** + * Enable or disable the given feature + * + * Only used for features which have simple enable/disable controls. + * + * @param feature the Lineage Hardware feature to set + * @param enable true to enable, false to disale + * + * @return true if the feature is enabled, false otherwise. + */ + public boolean set(int feature, boolean enable) { + if (!BOOLEAN_FEATURES.contains(feature)) { + throw new IllegalArgumentException(feature + " is not a boolean"); + } + + try { + if (isSupportedHIDL(feature)) { + IBase obj = mHIDLMap.get(feature); + switch (feature) { + case FEATURE_HIGH_TOUCH_SENSITIVITY: + IGloveMode gloveMode = (IGloveMode) obj; + return gloveMode.setEnabled(enable); + case FEATURE_KEY_DISABLE: + IKeyDisabler keyDisabler = (IKeyDisabler) obj; + return keyDisabler.setEnabled(enable); + case FEATURE_TOUCH_HOVERING: + IStylusMode stylusMode = (IStylusMode) obj; + return stylusMode.setEnabled(enable); + } + } + } catch (RemoteException e) { + } + return false; + } + + /** + * @return a list of available touchscreen gestures on the devices + */ + public TouchscreenGesture[] getTouchscreenGestures() { + try { + if (isSupportedHIDL(FEATURE_TOUCHSCREEN_GESTURES)) { + ITouchscreenGesture touchscreenGesture = (ITouchscreenGesture) + mHIDLMap.get(FEATURE_TOUCHSCREEN_GESTURES); + return HIDLHelper.fromHIDLGestures(touchscreenGesture.getSupportedGestures()); + } + } catch (RemoteException e) { + } + return null; + } + + /** + * @return true if setting the activation status was successful + */ + public boolean setTouchscreenGestureEnabled( + TouchscreenGesture gesture, boolean state) { + try { + if (isSupportedHIDL(FEATURE_TOUCHSCREEN_GESTURES)) { + ITouchscreenGesture touchscreenGesture = (ITouchscreenGesture) + mHIDLMap.get(FEATURE_TOUCHSCREEN_GESTURES); + return touchscreenGesture.setGestureEnabled( + HIDLHelper.toHIDLGesture(gesture), state); + } + } catch (RemoteException e) { + } + return false; + } +} diff --git a/core/java/com/android/internal/bootleggers/hardware/TouchscreenGesture.aidl b/core/java/com/android/internal/bootleggers/hardware/TouchscreenGesture.aidl new file mode 100644 index 000000000000..e7853bf403b5 --- /dev/null +++ b/core/java/com/android/internal/bootleggers/hardware/TouchscreenGesture.aidl @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2016 The CyanogenMod Project + * 2017 The LineageOS 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.bootleggers.hardware; + +parcelable TouchscreenGesture; diff --git a/core/java/com/android/internal/bootleggers/hardware/TouchscreenGesture.java b/core/java/com/android/internal/bootleggers/hardware/TouchscreenGesture.java new file mode 100644 index 000000000000..764aab7967b7 --- /dev/null +++ b/core/java/com/android/internal/bootleggers/hardware/TouchscreenGesture.java @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2016 The CyanogenMod Project + * 2017 The LineageOS 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.bootleggers.hardware; + +import android.os.Parcel; +import android.os.Parcelable; + +/** + * Touchscreen gestures API + * + * A device may implement several touchscreen gestures for use while + * the display is turned off, such as drawing alphabets and shapes. + * These gestures can be interpreted by userspace to activate certain + * actions and launch certain apps, such as to skip music tracks, + * to turn on the flashlight, or to launch the camera app. + * + * This *should always* be supported by the hardware directly. + * A lot of recent touch controllers have a firmware option for this. + * + * This API provides support for enumerating the gestures + * supported by the touchscreen. + * + * A TouchscreenGesture is referenced by it's identifier and carries an + * associated name (up to the user to translate this value). + */ +public class TouchscreenGesture implements Parcelable { + + public final int id; + public final String name; + public final int keycode; + + public TouchscreenGesture(int id, String name, int keycode) { + this.id = id; + this.name = name; + this.keycode = keycode; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel parcel, int flags) { + parcel.writeInt(id); + parcel.writeString(name); + parcel.writeInt(keycode); + } + + /** @hide */ + public static final Parcelable.Creator CREATOR = + new Parcelable.Creator() { + + public TouchscreenGesture createFromParcel(Parcel in) { + return new TouchscreenGesture(in.readInt(), in.readString(), in.readInt()); + } + + @Override + public TouchscreenGesture[] newArray(int size) { + return new TouchscreenGesture[size]; + } + }; +} diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index ec2bc7cde1a0..a8e39eb5e994 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -3634,6 +3634,7 @@ public T stopObject(String name, long elapsedRealtimeMs) { public abstract T instantiateObject(); } + @SuppressWarnings("ParcelableCreator") public static class ControllerActivityCounterImpl extends ControllerActivityCounter implements Parcelable { private final Clock mClock; diff --git a/core/java/com/android/internal/os/BinderCallsStats.java b/core/java/com/android/internal/os/BinderCallsStats.java index 0a29fc5285a5..eb62cb07ee68 100644 --- a/core/java/com/android/internal/os/BinderCallsStats.java +++ b/core/java/com/android/internal/os/BinderCallsStats.java @@ -735,7 +735,7 @@ protected long getElapsedRealtimeMicro() { } protected boolean shouldRecordDetailedData() { - return mRandom.nextInt() % mPeriodicSamplingInterval == 0; + return mRandom.nextInt(mPeriodicSamplingInterval) == 0; } /** diff --git a/core/java/com/android/internal/os/BinderLatencyObserver.java b/core/java/com/android/internal/os/BinderLatencyObserver.java index e9d55db3a5b4..1276fb980799 100644 --- a/core/java/com/android/internal/os/BinderLatencyObserver.java +++ b/core/java/com/android/internal/os/BinderLatencyObserver.java @@ -236,7 +236,7 @@ protected boolean shouldCollect(LatencyDims dims) { } protected boolean shouldKeepSample() { - return mRandom.nextInt() % mPeriodicSamplingInterval == 0; + return mRandom.nextInt(mPeriodicSamplingInterval) == 0; } /** Updates the sampling interval. */ diff --git a/core/java/com/android/internal/os/DeviceKeyHandler.java b/core/java/com/android/internal/os/DeviceKeyHandler.java new file mode 100644 index 000000000000..8902337f3ebb --- /dev/null +++ b/core/java/com/android/internal/os/DeviceKeyHandler.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2012 The CyanogenMod Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.os; + +import android.view.KeyEvent; + +public interface DeviceKeyHandler { + + /** + * Invoked when an unknown key was detected by the system, letting the device handle + * this special keys prior to pass the key to the active app. + * + * @param event The key event to be handled + * @return null if event is consumed, KeyEvent to be handled otherwise + */ + public KeyEvent handleKeyEvent(KeyEvent event); +} diff --git a/core/java/com/android/internal/os/KernelCpuUidTimeReader.java b/core/java/com/android/internal/os/KernelCpuUidTimeReader.java index c801be0ce3e7..7ea55dcbe67f 100644 --- a/core/java/com/android/internal/os/KernelCpuUidTimeReader.java +++ b/core/java/com/android/internal/os/KernelCpuUidTimeReader.java @@ -20,6 +20,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; +import android.os.Build; import android.os.StrictMode; import android.util.IntArray; import android.util.Slog; @@ -497,7 +498,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; } @@ -530,7 +531,11 @@ void readDeltaImpl(@Nullable Callback cb, boolean forceRead) { CharBuffer buf; while ((buf = iter.nextLine()) != null) { if (asLongs(buf, mBuffer) != mBuffer.length) { - Slog.wtf(mTag, "Invalid line: " + buf.toString()); + if (Build.IS_ENG) { + Slog.wtf(mTag, "Invalid line: " + buf.toString()); + } else { + Slog.w(mTag, "Invalid line: " + buf.toString()); + } continue; } processUidDelta(cb); @@ -673,7 +678,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/LooperStats.java b/core/java/com/android/internal/os/LooperStats.java index 2805dccffe50..0645eb7f0835 100644 --- a/core/java/com/android/internal/os/LooperStats.java +++ b/core/java/com/android/internal/os/LooperStats.java @@ -290,7 +290,7 @@ protected long getSystemUptimeMillis() { } protected boolean shouldCollectDetailedData() { - return ThreadLocalRandom.current().nextInt() % mSamplingInterval == 0; + return ThreadLocalRandom.current().nextInt(mSamplingInterval) == 0; } private static class DispatchSession { diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java index ca1ae194cb12..319c091f4237 100644 --- a/core/java/com/android/internal/os/ZygoteInit.java +++ b/core/java/com/android/internal/os/ZygoteInit.java @@ -187,6 +187,12 @@ private static void preloadSharedLibraries() { System.loadLibrary("android"); System.loadLibrary("compiler_rt"); System.loadLibrary("jnigraphics"); + + try { + System.loadLibrary("qti_performance"); + } catch (UnsatisfiedLinkError e) { + Log.e(TAG, "Couldn't load qti_performance"); + } } native private static void nativePreloadAppProcessHALs(); diff --git a/core/java/com/android/internal/policy/GestureNavigationSettingsObserver.java b/core/java/com/android/internal/policy/GestureNavigationSettingsObserver.java index 205c5fd735ea..ea5ab3b4155a 100644 --- a/core/java/com/android/internal/policy/GestureNavigationSettingsObserver.java +++ b/core/java/com/android/internal/policy/GestureNavigationSettingsObserver.java @@ -67,6 +67,9 @@ public void register() { r.registerContentObserver( Settings.Secure.getUriFor(Settings.Secure.USER_SETUP_COMPLETE), false, this, UserHandle.USER_ALL); + r.registerContentObserver(Settings.System.getUriFor( + Settings.System.BACK_GESTURE_HAPTIC), + false, this, UserHandle.USER_ALL); DeviceConfig.addOnPropertiesChangedListener( DeviceConfig.NAMESPACE_SYSTEMUI, runnable -> mMainHandler.post(runnable), @@ -94,6 +97,15 @@ public int getRightSensitivity(Resources userRes) { return getSensitivity(userRes, Settings.Secure.BACK_GESTURE_INSET_SCALE_RIGHT); } + public boolean getEdgeHaptic() { + return (Settings.System.getIntForUser( + mContext.getContentResolver(), Settings.System.BACK_GESTURE_HAPTIC, 0, + UserHandle.USER_CURRENT) == 1 && + Settings.System.getIntForUser( + mContext.getContentResolver(), Settings.System.HAPTIC_FEEDBACK_ENABLED, 0, + UserHandle.USER_CURRENT) == 1); + } + public boolean areNavigationButtonForcedVisible() { return Settings.Secure.getIntForUser(mContext.getContentResolver(), Settings.Secure.USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) == 0; @@ -114,4 +126,6 @@ private int getSensitivity(Resources userRes, String side) { mContext.getContentResolver(), side, 1.0f, UserHandle.USER_CURRENT); return (int) (inset * scale); } + + } diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl index 44cfe1aa4a79..dff07e9beeee 100644 --- a/core/java/com/android/internal/statusbar/IStatusBar.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl @@ -149,7 +149,7 @@ oneway interface IStatusBar void showPinningEnterExitToast(boolean entering); void showPinningEscapeToast(); - void showShutdownUi(boolean isReboot, String reason); + void showShutdownUi(boolean isReboot, String reason, boolean rebootCustom); /** * Used to show the authentication dialog (Biometrics, Device Credential). @@ -322,4 +322,9 @@ oneway interface IStatusBar /** Unregisters a nearby media devices provider. */ void unregisterNearbyMediaDevicesProvider(in INearbyMediaDevicesProvider provider); + + /** + * Toggles flashlight of the device + */ + void toggleCameraFlash(); } diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl index ef8f2db5ff57..479ca17724a6 100644 --- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl @@ -101,7 +101,7 @@ interface IStatusBarService * These methods are needed for global actions control which the UI is shown in sysui. */ void shutdown(); - void reboot(boolean safeMode); + void reboot(boolean safeMode, String reason); /** just restarts android without rebooting device. Used for some feature flags. */ void restart(); @@ -226,4 +226,9 @@ interface IStatusBarService /** Unregisters a nearby media devices provider. */ void unregisterNearbyMediaDevicesProvider(in INearbyMediaDevicesProvider provider); + + /** + * Toggles flashlight of the device + */ + void toggleCameraFlash(); } diff --git a/core/java/com/android/internal/util/LatencyTracker.java b/core/java/com/android/internal/util/LatencyTracker.java index 8fcb6d5514d4..4b7b91c74f94 100644 --- a/core/java/com/android/internal/util/LatencyTracker.java +++ b/core/java/com/android/internal/util/LatencyTracker.java @@ -468,7 +468,7 @@ public void logAction(@Action int action, int duration) { boolean shouldSample; int traceThreshold; synchronized (mLock) { - shouldSample = ThreadLocalRandom.current().nextInt() % mSamplingInterval == 0; + shouldSample = ThreadLocalRandom.current().nextInt(mSamplingInterval) == 0; traceThreshold = mTraceThresholdPerAction[action]; } diff --git a/core/java/com/android/internal/util/awaken/FileUtils.java b/core/java/com/android/internal/util/awaken/FileUtils.java new file mode 100644 index 000000000000..ee663668b9f5 --- /dev/null +++ b/core/java/com/android/internal/util/awaken/FileUtils.java @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2016 The CyanogenMod Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.util.awaken; + +import android.util.Log; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; + +public final class FileUtils { + private static final String TAG = "FileUtils"; + + private FileUtils() { + // This class is not supposed to be instantiated + } + + /** + * Reads the first line of text from the given file. + * Reference {@link BufferedReader#readLine()} for clarification on what a line is + * + * @return the read line contents, or null on failure + */ + public static String readOneLine(String fileName) { + String line = null; + BufferedReader reader = null; + + try { + reader = new BufferedReader(new FileReader(fileName), 512); + line = reader.readLine(); + } catch (FileNotFoundException e) { + Log.w(TAG, "No such file " + fileName + " for reading", e); + } catch (IOException e) { + Log.e(TAG, "Could not read from file " + fileName, e); + } finally { + try { + if (reader != null) { + reader.close(); + } + } catch (IOException e) { + // Ignored, not much we can do anyway + } + } + + return line; + } + + /** + * Writes the given value into the given file + * + * @return true on success, false on failure + */ + public static boolean writeLine(String fileName, String value) { + BufferedWriter writer = null; + + try { + writer = new BufferedWriter(new FileWriter(fileName)); + writer.write(value); + } catch (FileNotFoundException e) { + Log.w(TAG, "No such file " + fileName + " for writing", e); + return false; + } catch (IOException e) { + Log.e(TAG, "Could not write to file " + fileName, e); + return false; + } finally { + try { + if (writer != null) { + writer.close(); + } + } catch (IOException e) { + // Ignored, not much we can do anyway + } + } + + return true; + } + + /** + * Checks whether the given file exists + * + * @return true if exists, false if not + */ + public static boolean fileExists(String fileName) { + final File file = new File(fileName); + return file.exists(); + } + + /** + * Checks whether the given file is readable + * + * @return true if readable, false if not + */ + public static boolean isFileReadable(String fileName) { + final File file = new File(fileName); + return file.exists() && file.canRead(); + } + + /** + * Checks whether the given file is writable + * + * @return true if writable, false if not + */ + public static boolean isFileWritable(String fileName) { + final File file = new File(fileName); + return file.exists() && file.canWrite(); + } +} diff --git a/core/java/com/android/internal/util/bootleg/Action.java b/core/java/com/android/internal/util/bootleg/Action.java new file mode 100644 index 000000000000..f49534c7317a --- /dev/null +++ b/core/java/com/android/internal/util/bootleg/Action.java @@ -0,0 +1,307 @@ +/* +* Copyright (C) 2014 SlimRoms 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.bootleg; + +import android.app.Activity; +import android.app.ActivityManagerNative; +import android.app.SearchManager; +import android.content.ActivityNotFoundException; +import android.content.Context; +import android.content.Intent; +import android.hardware.camera2.CameraManager; +import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CameraAccessException; +import android.hardware.input.InputManager; +import android.media.AudioManager; +import android.media.session.MediaSessionLegacyHelper; +import android.media.ToneGenerator; +import android.net.Uri; +import android.os.PowerManager; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.SystemClock; +import android.os.UserHandle; +import android.os.Vibrator; +import android.provider.Settings; +import android.provider.MediaStore; +import android.util.Log; +import android.view.InputDevice; +import android.view.KeyCharacterMap; +import android.view.KeyEvent; +import android.view.WindowManagerGlobal; + +import java.net.URISyntaxException; + +public class Action { + + private static final int MSG_INJECT_KEY_DOWN = 1066; + private static final int MSG_INJECT_KEY_UP = 1067; + + private static boolean sTorchEnabled = false; + + public static void processAction(Context context, String action, boolean isLongpress) { + processActionWithOptions(context, action, isLongpress, true); + } + + public static void processActionWithOptions(Context context, + String action, boolean isLongpress, boolean collapseShade) { + + if (action == null || action.equals(ActionConstants.ACTION_NULL)) { + return; + } + + boolean isKeyguardShowing = false; + try { + isKeyguardShowing = + WindowManagerGlobal.getWindowManagerService().isKeyguardLocked(); + } catch (RemoteException e) { + Log.w("Action", "Error getting window manager service", e); + } + + // process the actions + if (action.equals(ActionConstants.ACTION_HOME)) { + triggerVirtualKeypress(KeyEvent.KEYCODE_HOME, isLongpress); + return; + } else if (action.equals(ActionConstants.ACTION_BACK)) { + triggerVirtualKeypress(KeyEvent.KEYCODE_BACK, isLongpress); + return; + } else if (action.equals(ActionConstants.ACTION_SEARCH)) { + triggerVirtualKeypress(KeyEvent.KEYCODE_SEARCH, isLongpress); + return; + } else if (action.equals(ActionConstants.ACTION_MENU) + || action.equals(ActionConstants.ACTION_MENU_BIG)) { + triggerVirtualKeypress(KeyEvent.KEYCODE_MENU, isLongpress); + return; + } else if (action.equals(ActionConstants.ACTION_IME_NAVIGATION_LEFT)) { + triggerVirtualKeypress(KeyEvent.KEYCODE_DPAD_LEFT, isLongpress); + return; + } else if (action.equals(ActionConstants.ACTION_IME_NAVIGATION_RIGHT)) { + triggerVirtualKeypress(KeyEvent.KEYCODE_DPAD_RIGHT, isLongpress); + return; + } else if (action.equals(ActionConstants.ACTION_IME_NAVIGATION_UP)) { + triggerVirtualKeypress(KeyEvent.KEYCODE_DPAD_UP, isLongpress); + return; + } else if (action.equals(ActionConstants.ACTION_IME_NAVIGATION_DOWN)) { + triggerVirtualKeypress(KeyEvent.KEYCODE_DPAD_DOWN, isLongpress); + return; + } else if (action.equals(ActionConstants.ACTION_TORCH)) { + try { + CameraManager cameraManager = (CameraManager) + context.getSystemService(Context.CAMERA_SERVICE); + for (final String cameraId : cameraManager.getCameraIdList()) { + CameraCharacteristics characteristics = + cameraManager.getCameraCharacteristics(cameraId); + Boolean flashAvailable = characteristics.get(CameraCharacteristics.FLASH_INFO_AVAILABLE); + int orient = characteristics.get(CameraCharacteristics.LENS_FACING); + if (flashAvailable != null && flashAvailable && orient == CameraCharacteristics.LENS_FACING_BACK) { + cameraManager.setTorchMode(cameraId, !sTorchEnabled); + sTorchEnabled = !sTorchEnabled; + break; + } + } + } catch (CameraAccessException e) { + } + return; + } else if (action.equals(ActionConstants.ACTION_POWER)) { + PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); + pm.goToSleep(SystemClock.uptimeMillis()); + return; + } else if (action.equals(ActionConstants.ACTION_IME)) { + if (isKeyguardShowing) { + return; + } + context.sendBroadcastAsUser( + new Intent("android.settings.SHOW_INPUT_METHOD_PICKER"), + new UserHandle(UserHandle.USER_CURRENT)); + return; + } else if (action.equals(ActionConstants.ACTION_VOICE_SEARCH)) { + // launch the search activity + Intent intent = new Intent(Intent.ACTION_SEARCH_LONG_PRESS); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + try { + // TODO: This only stops the factory-installed search manager. + // Need to formalize an API to handle others + SearchManager searchManager = + (SearchManager) context.getSystemService(Context.SEARCH_SERVICE); + if (searchManager != null) { + searchManager.stopSearch(); + } + startActivity(context, intent); + } catch (ActivityNotFoundException e) { + Log.e("gzospActions:", "No activity to handle assist long press action.", e); + } + return; + } else if (action.equals(ActionConstants.ACTION_VIB)) { + AudioManager am = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); + if(am != null && ActivityManagerNative.isSystemReady()) { + if(am.getRingerMode() != AudioManager.RINGER_MODE_VIBRATE) { + am.setRingerMode(AudioManager.RINGER_MODE_VIBRATE); + Vibrator vib = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE); + if(vib != null){ + vib.vibrate(50); + } + }else{ + am.setRingerMode(AudioManager.RINGER_MODE_NORMAL); + ToneGenerator tg = new ToneGenerator( + AudioManager.STREAM_NOTIFICATION, + (int)(ToneGenerator.MAX_VOLUME * 0.85)); + if(tg != null){ + tg.startTone(ToneGenerator.TONE_PROP_BEEP); + } + } + } + return; + } else if (action.equals(ActionConstants.ACTION_SILENT)) { + AudioManager am = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); + if (am != null && ActivityManagerNative.isSystemReady()) { + if (am.getRingerMode() != AudioManager.RINGER_MODE_SILENT) { + am.setRingerMode(AudioManager.RINGER_MODE_SILENT); + } else { + am.setRingerMode(AudioManager.RINGER_MODE_NORMAL); + ToneGenerator tg = new ToneGenerator( + AudioManager.STREAM_NOTIFICATION, + (int)(ToneGenerator.MAX_VOLUME * 0.85)); + if (tg != null) { + tg.startTone(ToneGenerator.TONE_PROP_BEEP); + } + } + } + return; + } else if (action.equals(ActionConstants.ACTION_VIB_SILENT)) { + AudioManager am = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); + if (am != null && ActivityManagerNative.isSystemReady()) { + if (am.getRingerMode() == AudioManager.RINGER_MODE_NORMAL) { + am.setRingerMode(AudioManager.RINGER_MODE_VIBRATE); + Vibrator vib = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE); + if (vib != null) { + vib.vibrate(50); + } + } else if (am.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE) { + am.setRingerMode(AudioManager.RINGER_MODE_SILENT); + } else { + am.setRingerMode(AudioManager.RINGER_MODE_NORMAL); + ToneGenerator tg = new ToneGenerator( + AudioManager.STREAM_NOTIFICATION, + (int)(ToneGenerator.MAX_VOLUME * 0.85)); + if (tg != null) { + tg.startTone(ToneGenerator.TONE_PROP_BEEP); + } + } + } + return; + } else if (action.equals(ActionConstants.ACTION_CAMERA)) { + // ToDo: Send for secure keyguard secure camera intent. + // We need to add support for it first. + Intent intent = new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA, null); + startActivity(context, intent); + return; + } else if (action.equals(ActionConstants.ACTION_MEDIA_PREVIOUS)) { + dispatchMediaKeyWithWakeLock(KeyEvent.KEYCODE_MEDIA_PREVIOUS, context); + return; + } else if (action.equals(ActionConstants.ACTION_MEDIA_NEXT)) { + dispatchMediaKeyWithWakeLock(KeyEvent.KEYCODE_MEDIA_NEXT, context); + return; + } else if (action.equals(ActionConstants.ACTION_MEDIA_PLAY_PAUSE)) { + dispatchMediaKeyWithWakeLock(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE, context); + return; + } else if (action.equals(ActionConstants.ACTION_WAKE_DEVICE)) { + PowerManager powerManager = + (PowerManager) context.getSystemService(Context.POWER_SERVICE); + if (!powerManager.isScreenOn()) { + powerManager.wakeUp(SystemClock.uptimeMillis()); + } + return; + } else { + // we must have a custom uri + Intent intent = null; + try { + intent = Intent.parseUri(action, 0); + } catch (URISyntaxException e) { + Log.e("gzospActions:", "URISyntaxException: [" + action + "]"); + return; + } + startActivity(context, intent); + return; + } + + } + + public static boolean isActionKeyEvent(String action) { + if (action.equals(ActionConstants.ACTION_HOME) + || action.equals(ActionConstants.ACTION_BACK) + || action.equals(ActionConstants.ACTION_SEARCH) + || action.equals(ActionConstants.ACTION_MENU) + || action.equals(ActionConstants.ACTION_MENU_BIG) + || action.equals(ActionConstants.ACTION_NULL)) { + return true; + } + return false; + } + + private static void startActivity(Context context, Intent intent) { + if (intent == null) { + return; + } + intent.addFlags( + Intent.FLAG_ACTIVITY_NEW_TASK + | Intent.FLAG_ACTIVITY_SINGLE_TOP + | Intent.FLAG_ACTIVITY_CLEAR_TOP); + context.startActivityAsUser(intent, + new UserHandle(UserHandle.USER_CURRENT)); + } + + private static void dispatchMediaKeyWithWakeLock(int keycode, Context context) { + if (ActivityManagerNative.isSystemReady()) { + KeyEvent event = new KeyEvent(SystemClock.uptimeMillis(), + SystemClock.uptimeMillis(), KeyEvent.ACTION_DOWN, keycode, 0); + MediaSessionLegacyHelper.getHelper(context).sendMediaButtonEvent(event, true); + event = KeyEvent.changeAction(event, KeyEvent.ACTION_UP); + MediaSessionLegacyHelper.getHelper(context).sendMediaButtonEvent(event, true); + } + } + + public static void triggerVirtualKeypress(final int keyCode, boolean longpress) { + InputManager im = InputManager.getInstance(); + long now = SystemClock.uptimeMillis(); + int downflags = 0; + int upflags = 0; + if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT + || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT + || keyCode == KeyEvent.KEYCODE_DPAD_UP + || keyCode == KeyEvent.KEYCODE_DPAD_DOWN) { + downflags = upflags = KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE; + } else { + downflags = upflags = KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY; + } + if (longpress) { + downflags |= KeyEvent.FLAG_LONG_PRESS; + } + + final KeyEvent downEvent = new KeyEvent(now, now, KeyEvent.ACTION_DOWN, + keyCode, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, + downflags, + InputDevice.SOURCE_KEYBOARD); + im.injectInputEvent(downEvent, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC); + + final KeyEvent upEvent = new KeyEvent(now, now, KeyEvent.ACTION_UP, + keyCode, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, + upflags, + InputDevice.SOURCE_KEYBOARD); + im.injectInputEvent(upEvent, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC); + } + +} diff --git a/core/java/com/android/internal/util/bootleg/ActionConfig.java b/core/java/com/android/internal/util/bootleg/ActionConfig.java new file mode 100644 index 000000000000..36b6e19eea7b --- /dev/null +++ b/core/java/com/android/internal/util/bootleg/ActionConfig.java @@ -0,0 +1,81 @@ +/* +* Copyright (C) 2014 SlimRoms 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.bootleg; + +public class ActionConfig { + + private String mClickAction; + private String mClickActionDescription; + private String mLongpressAction; + private String mLongpressActionDescription; + private String mIconUri; + + public ActionConfig(String clickAction, String clickActionDescription, + String longpressAction, String longpressActionDescription, String iconUri) { + mClickAction = clickAction; + mClickActionDescription = clickActionDescription; + mLongpressAction = longpressAction; + mLongpressActionDescription = longpressActionDescription; + mIconUri = iconUri; + } + + @Override + public String toString() { + return mClickActionDescription; + } + + public String getClickAction() { + return mClickAction; + } + + public String getClickActionDescription() { + return mClickActionDescription; + } + + public String getLongpressAction() { + return mLongpressAction; + } + + public String getLongpressActionDescription() { + return mLongpressActionDescription; + } + + public String getIcon() { + return mIconUri; + } + + public void setClickAction(String action) { + mClickAction = action; + } + + public void setClickActionDescription(String description) { + mClickActionDescription = description; + } + + public void setLongpressAction(String action) { + mLongpressAction = action; + } + + public void setLongpressActionDescription(String description) { + mLongpressActionDescription = description; + } + + public void setIcon(String iconUri) { + mIconUri = iconUri; + } + +} diff --git a/core/java/com/android/internal/util/bootleg/ActionConstants.java b/core/java/com/android/internal/util/bootleg/ActionConstants.java new file mode 100644 index 000000000000..fa947c30bbe3 --- /dev/null +++ b/core/java/com/android/internal/util/bootleg/ActionConstants.java @@ -0,0 +1,102 @@ +/* +* Copyright (C) 2013 SlimRoms 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.bootleg; + +public class ActionConstants { + + // key must fit with the values arrays from Settings to use + // SlimActions.java actions + public static final String ACTION_HOME = "**home**"; + public static final String ACTION_BACK = "**back**"; + public static final String ACTION_SEARCH = "**search**"; + public static final String ACTION_VOICE_SEARCH = "**voice_search**"; + public static final String ACTION_MENU = "**menu**"; + public static final String ACTION_MENU_BIG = "**menu_big**"; + public static final String ACTION_POWER = "**power**"; + public static final String ACTION_NOTIFICATIONS = "**notifications**"; + public static final String ACTION_RECENTS = "**recents**"; + public static final String ACTION_SCREENSHOT = "**screenshot**"; + public static final String ACTION_IME = "**ime**"; + public static final String ACTION_LAST_APP = "**lastapp**"; + public static final String ACTION_KILL = "**kill**"; + public static final String ACTION_ASSIST = "**assist**"; + public static final String ACTION_VIB = "**ring_vib**"; + public static final String ACTION_SILENT = "**ring_silent**"; + public static final String ACTION_VIB_SILENT = "**ring_vib_silent**"; + public static final String ACTION_POWER_MENU = "**power_menu**"; + public static final String ACTION_TORCH = "**torch**"; + public static final String ACTION_EXPANDED_DESKTOP = "**expanded_desktop**"; + public static final String ACTION_THEME_SWITCH = "**theme_switch**"; + public static final String ACTION_KEYGUARD_SEARCH = "**keyguard_search**"; + public static final String ACTION_PIE = "**pie**"; + public static final String ACTION_NAVBAR = "**nav_bar**"; + public static final String ACTION_IME_NAVIGATION_LEFT = "**ime_nav_left**"; + public static final String ACTION_IME_NAVIGATION_RIGHT = "**ime_nav_right**"; + public static final String ACTION_IME_NAVIGATION_UP = "**ime_nav_up**"; + public static final String ACTION_IME_NAVIGATION_DOWN = "**ime_nav_down**"; + public static final String ACTION_CAMERA = "**camera**"; + public static final String ACTION_MEDIA_PREVIOUS = "**media_previous**"; + public static final String ACTION_MEDIA_NEXT = "**media_next**"; + public static final String ACTION_MEDIA_PLAY_PAUSE = "**media_play_pause**"; + public static final String ACTION_WAKE_DEVICE = "**wake_device**"; + + // no action + public static final String ACTION_NULL = "**null**"; + + // this shorcut constant is only used to identify if the user + // selected in settings a custom app...after it is choosed intent uri + // is saved in the ButtonConfig object + public static final String ACTION_APP = "**app**"; + + public static final String ICON_EMPTY = "empty"; + public static final String SYSTEM_ICON_IDENTIFIER = "system_shortcut="; + public static final String ACTION_DELIMITER = "|"; + + public static final String NAVIGATION_CONFIG_DEFAULT = + ACTION_BACK + ACTION_DELIMITER + + ACTION_NULL + ACTION_DELIMITER + + ICON_EMPTY + ACTION_DELIMITER + + ACTION_HOME + ACTION_DELIMITER + + ACTION_NULL + ACTION_DELIMITER + + ICON_EMPTY + ACTION_DELIMITER + + ACTION_RECENTS + ACTION_DELIMITER + + ACTION_NULL + ACTION_DELIMITER + + ICON_EMPTY; + + public static final String NAV_RING_CONFIG_DEFAULT = + ACTION_ASSIST + ACTION_DELIMITER + + ACTION_NULL + ACTION_DELIMITER + + ICON_EMPTY; + + public static final String PIE_SECOND_LAYER_CONFIG_DEFAULT = + ACTION_POWER_MENU + ACTION_DELIMITER + + ACTION_NULL + ACTION_DELIMITER + + ICON_EMPTY + ACTION_DELIMITER + + ACTION_NOTIFICATIONS + ACTION_DELIMITER + + ACTION_NULL + ACTION_DELIMITER + + ICON_EMPTY + ACTION_DELIMITER + + ACTION_SEARCH + ACTION_DELIMITER + + ACTION_NULL + ACTION_DELIMITER + + ICON_EMPTY + ACTION_DELIMITER + + ACTION_SCREENSHOT + ACTION_DELIMITER + + ACTION_NULL + ACTION_DELIMITER + + ICON_EMPTY + ACTION_DELIMITER + + ACTION_IME + ACTION_DELIMITER + + ACTION_NULL + ACTION_DELIMITER + + ICON_EMPTY; + +} diff --git a/core/java/com/android/internal/util/bootleg/ActionHelper.java b/core/java/com/android/internal/util/bootleg/ActionHelper.java new file mode 100644 index 000000000000..0077d1711e78 --- /dev/null +++ b/core/java/com/android/internal/util/bootleg/ActionHelper.java @@ -0,0 +1,162 @@ +/* +* Copyright (C) 2014 SlimRoms 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.bootleg; + +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.res.Resources; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.provider.Settings; +import android.os.UserHandle; +import android.util.Log; + +import java.io.File; +import java.io.FileNotFoundException; +import java.net.URISyntaxException; +import java.util.ArrayList; + +public class ActionHelper { + + private static final String SYSTEMUI_METADATA_NAME = "com.android.systemui"; + + // General methods to retrieve the correct icon for the respective action. + public static Drawable getButtonIconImage(Context context, + String clickAction, String customIcon) { + int resId = -1; + Drawable d = null; + PackageManager pm = context.getPackageManager(); + if (pm == null) { + return null; + } + + Resources systemUiResources; + try { + systemUiResources = pm.getResourcesForApplication(SYSTEMUI_METADATA_NAME); + } catch (Exception e) { + Log.e("ButtonsHelper:", "can't access systemui resources",e); + return null; + } + + if (!clickAction.startsWith("**")) { + try { + String extraIconPath = clickAction.replaceAll(".*?hasExtraIcon=", ""); + if (extraIconPath != null && !extraIconPath.isEmpty()) { + File f = new File(Uri.parse(extraIconPath).getPath()); + if (f.exists()) { + d = new BitmapDrawable(context.getResources(), + f.getAbsolutePath()); + } + } + if (d == null) { + d = pm.getActivityIcon(Intent.parseUri(clickAction, 0)); + } + } catch (NameNotFoundException e) { + resId = systemUiResources.getIdentifier( + SYSTEMUI_METADATA_NAME + ":drawable/ic_sysbar_null", null, null); + if (resId > 0) { + d = systemUiResources.getDrawable(resId); + return d; + } + } catch (URISyntaxException e) { + e.printStackTrace(); + } + } + + if (customIcon != null && customIcon.startsWith(ActionConstants.SYSTEM_ICON_IDENTIFIER)) { + resId = systemUiResources.getIdentifier(customIcon.substring( + ActionConstants.SYSTEM_ICON_IDENTIFIER.length()), "drawable", "android"); + if (resId > 0) { + return systemUiResources.getDrawable(resId); + } + } else if (customIcon != null && !customIcon.equals(ActionConstants.ICON_EMPTY)) { + File f = new File(Uri.parse(customIcon).getPath()); + if (f.exists()) { + return new BitmapDrawable(context.getResources(), + ImageHelper.getRoundedCornerBitmap( + new BitmapDrawable(context.getResources(), + f.getAbsolutePath()).getBitmap())); + } else { + Log.e("ActionHelper:", "can't access custom icon image"); + return null; + } + } else if (clickAction.startsWith("**")) { + resId = getActionSystemIcon(systemUiResources, clickAction); + + if (resId > 0) { + return systemUiResources.getDrawable(resId); + } + } + return d; + } + + private static int getActionSystemIcon(Resources systemUiResources, String clickAction) { + int resId = -1; + + // ToDo: Add the resources to SystemUI. + if (clickAction.equals(ActionConstants.ACTION_HOME)) { + resId = systemUiResources.getIdentifier( + SYSTEMUI_METADATA_NAME + ":drawable/ic_sysbar_home", null, null); + } else if (clickAction.equals(ActionConstants.ACTION_BACK)) { + resId = systemUiResources.getIdentifier( + SYSTEMUI_METADATA_NAME + ":drawable/ic_sysbar_back", null, null); + } else if (clickAction.equals(ActionConstants.ACTION_RECENTS)) { + resId = systemUiResources.getIdentifier( + SYSTEMUI_METADATA_NAME + ":drawable/ic_sysbar_recent", null, null); + } else if (clickAction.equals(ActionConstants.ACTION_SEARCH) + || clickAction.equals(ActionConstants.ACTION_ASSIST)) { + resId = systemUiResources.getIdentifier( + SYSTEMUI_METADATA_NAME + ":drawable/ic_sysbar_search", null, null); + } else if (clickAction.equals(ActionConstants.ACTION_KEYGUARD_SEARCH)) { + resId = systemUiResources.getIdentifier( + SYSTEMUI_METADATA_NAME + ":drawable/ic_sysbar_search_light", null, null); + } else if (clickAction.equals(ActionConstants.ACTION_MENU)) { + resId = systemUiResources.getIdentifier( + SYSTEMUI_METADATA_NAME + ":drawable/ic_sysbar_menu", null, null); + } else if (clickAction.equals(ActionConstants.ACTION_MENU_BIG)) { + resId = systemUiResources.getIdentifier( + SYSTEMUI_METADATA_NAME + ":drawable/ic_sysbar_menu_big", null, null); + } else if (clickAction.equals(ActionConstants.ACTION_IME)) { + resId = systemUiResources.getIdentifier( + SYSTEMUI_METADATA_NAME + ":drawable/ic_sysbar_ime_switcher", null, null); + } else if (clickAction.equals(ActionConstants.ACTION_POWER)) { + resId = systemUiResources.getIdentifier( + SYSTEMUI_METADATA_NAME + ":drawable/ic_sysbar_power", null, null); + } else if (clickAction.equals(ActionConstants.ACTION_POWER_MENU)) { + resId = systemUiResources.getIdentifier( + SYSTEMUI_METADATA_NAME + ":drawable/ic_sysbar_power_menu", null, null); + } else if (clickAction.equals(ActionConstants.ACTION_VIB)) { + resId = systemUiResources.getIdentifier( + SYSTEMUI_METADATA_NAME + ":drawable/ic_sysbar_vib", null, null); + } else if (clickAction.equals(ActionConstants.ACTION_SILENT)) { + resId = systemUiResources.getIdentifier( + SYSTEMUI_METADATA_NAME + ":drawable/ic_sysbar_silent", null, null); + } else if (clickAction.equals(ActionConstants.ACTION_VIB_SILENT)) { + resId = systemUiResources.getIdentifier( + SYSTEMUI_METADATA_NAME + ":drawable/ic_sysbar_ring_vib_silent", null, null); + } else { + resId = systemUiResources.getIdentifier( + SYSTEMUI_METADATA_NAME + ":drawable/ic_sysbar_null", null, null); + } + return resId; + } + +} diff --git a/core/java/com/android/internal/util/bootleg/AppHelper.java b/core/java/com/android/internal/util/bootleg/AppHelper.java new file mode 100644 index 000000000000..217bb4d1dbcc --- /dev/null +++ b/core/java/com/android/internal/util/bootleg/AppHelper.java @@ -0,0 +1,138 @@ +/* +* Copyright (C) 2013 gzosp 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.bootleg; + +import android.content.Context; +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.pm.PackageManager; +import android.content.res.Resources; +import android.util.Log; + +import java.net.URISyntaxException; + +public class AppHelper { + + private static final String SETTINGS_METADATA_NAME = "com.android.settings"; + + public static String getProperSummary(Context context, PackageManager pm, + Resources settingsResources, String action, String values, String entries) { + + if (pm == null || settingsResources == null || action == null) { + return context.getResources().getString( + com.android.internal.R.string.error_message_title); + } + + if (values != null && entries != null) { + int resIdEntries = -1; + int resIdValues = -1; + + resIdEntries = settingsResources.getIdentifier( + SETTINGS_METADATA_NAME + ":array/" + entries, null, null); + + resIdValues = settingsResources.getIdentifier( + SETTINGS_METADATA_NAME + ":array/" + values, null, null); + + if (resIdEntries > 0 && resIdValues > 0) { + try { + String[] entriesArray = settingsResources.getStringArray(resIdEntries); + String[] valuesArray = settingsResources.getStringArray(resIdValues); + for (int i = 0; i < valuesArray.length; i++) { + if (action.equals(valuesArray[i])) { + return entriesArray[i]; + } + } + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + return getFriendlyNameForUri(context, pm, action); + } + + public static String getFriendlyActivityName(Context context, + PackageManager pm, Intent intent, boolean labelOnly) { + ActivityInfo ai = intent.resolveActivityInfo(pm, PackageManager.GET_ACTIVITIES); + String friendlyName = null; + + if (ai != null) { + friendlyName = ai.loadLabel(pm).toString(); + if (friendlyName == null && !labelOnly) { + friendlyName = ai.name; + } + } + + if (friendlyName == null || friendlyName.startsWith("#Intent;")) { + return context.getResources().getString( + com.android.internal.R.string.error_message_title); + } + return friendlyName != null || labelOnly ? friendlyName : intent.toUri(0); + } + + public static String getFriendlyShortcutName( + Context context, PackageManager pm, Intent intent) { + String activityName = getFriendlyActivityName(context, pm, intent, true); + String name = intent.getStringExtra(Intent.EXTRA_SHORTCUT_NAME); + + if (activityName == null || activityName.startsWith("#Intent;")) { + return context.getResources().getString( + com.android.internal.R.string.error_message_title); + } + if (activityName != null && name != null) { + return activityName + ": " + name; + } + return name != null ? name : intent.toUri(0); + } + + public static String getFriendlyNameForUri( + Context context, PackageManager pm, String uri) { + if (uri == null || uri.startsWith("**")) { + return null; + } + + try { + Intent intent = Intent.parseUri(uri, 0); + if (Intent.ACTION_MAIN.equals(intent.getAction())) { + return getFriendlyActivityName(context, pm, intent, false); + } + return getFriendlyShortcutName(context, pm, intent); + } catch (URISyntaxException e) { + } + + return uri; + } + + public static String getShortcutPreferred( + Context context, PackageManager pm, String uri) { + if (uri == null || uri.startsWith("**")) { + return null; + } + try { + Intent intent = Intent.parseUri(uri, 0); + String name = intent.getStringExtra(Intent.EXTRA_SHORTCUT_NAME); + if (name == null || name.startsWith("#Intent;")) { + return getFriendlyActivityName(context, pm, intent, false); + } + return name; + } catch (URISyntaxException e) { + } + + return uri; + } + +} diff --git a/core/java/com/android/internal/util/bootleg/BootlegUtils.java b/core/java/com/android/internal/util/bootleg/BootlegUtils.java new file mode 100644 index 000000000000..63931a1649cf --- /dev/null +++ b/core/java/com/android/internal/util/bootleg/BootlegUtils.java @@ -0,0 +1,248 @@ +/* + * 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.bootleg; + +import android.app.ActivityManager; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.res.Resources; +import android.hardware.camera2.CameraAccessException; +import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CameraManager; +import android.net.ConnectivityManager; +import android.net.Network; +import android.net.NetworkCapabilities; +import android.hardware.input.InputManager; +import android.os.Handler; +import android.os.Looper; +import android.os.PowerManager; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.SystemClock; +import android.text.TextUtils; +import android.view.DisplayInfo; +import android.util.DisplayMetrics; +import android.view.InputDevice; +import android.view.KeyCharacterMap; +import android.view.KeyEvent; +import android.view.WindowManager; + +import com.android.internal.R; + +import java.util.List; +import java.util.Locale; + +public class BootlegUtils { + + private static int sDeviceType = -1; + private static final int DEVICE_PHONE = 0; + private static final int DEVICE_HYBRID = 1; + private static final int DEVICE_TABLET = 2; + + public static boolean isChineseLanguage() { + return Resources.getSystem().getConfiguration().locale.getLanguage().startsWith( + Locale.CHINESE.getLanguage()); + } + + public static boolean deviceSupportsFlashLight(Context context) { + CameraManager cameraManager = (CameraManager) context.getSystemService( + Context.CAMERA_SERVICE); + try { + String[] ids = cameraManager.getCameraIdList(); + for (String id : ids) { + CameraCharacteristics c = cameraManager.getCameraCharacteristics(id); + Boolean flashAvailable = c.get(CameraCharacteristics.FLASH_INFO_AVAILABLE); + Integer lensFacing = c.get(CameraCharacteristics.LENS_FACING); + if (flashAvailable != null + && flashAvailable + && lensFacing != null + && lensFacing == CameraCharacteristics.LENS_FACING_BACK) { + return true; + } + } + } catch (CameraAccessException e) { + // Ignore + } + return false; + } + + public static boolean isWifiOnly(Context context) { + ConnectivityManager cm = (ConnectivityManager)context.getSystemService( + Context.CONNECTIVITY_SERVICE); + final Network activeNetwork = cm.getActiveNetwork(); + if (activeNetwork == null) { + return true; + } + final NetworkCapabilities networkCapabilities = + cm.getNetworkCapabilities(activeNetwork); + if (networkCapabilities == null) { + return true; + } + return (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) == false); + } + + public static boolean isPackageInstalled(Context context, String pkg, boolean ignoreState) { + if (pkg != null) { + try { + PackageInfo pi = context.getPackageManager().getPackageInfo(pkg, 0); + if (!pi.applicationInfo.enabled && !ignoreState) { + return false; + } + } catch (NameNotFoundException e) { + return false; + } + } + + return true; + } + + public static boolean isPackageInstalled(Context context, String pkg) { + return isPackageInstalled(context, pkg, true); + } + + public static boolean isPackageAvailable(Context context, String pkg) { + return isPackageInstalled(context, pkg, false); + } + + private static int getScreenType(Context context) { + if (sDeviceType == -1) { + WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); + DisplayInfo outDisplayInfo = new DisplayInfo(); + wm.getDefaultDisplay().getDisplayInfo(outDisplayInfo); + int shortSize = Math.min(outDisplayInfo.logicalHeight, outDisplayInfo.logicalWidth); + int shortSizeDp = shortSize * DisplayMetrics.DENSITY_DEFAULT + / outDisplayInfo.logicalDensityDpi; + if (shortSizeDp < 600) { + // 0-599dp: "phone" UI with a separate status & navigation bar + sDeviceType = DEVICE_PHONE; + } else if (shortSizeDp < 720) { + // 600-719dp: "phone" UI with modifications for larger screens + sDeviceType = DEVICE_HYBRID; + } else { + // 720dp: "tablet" UI with a single combined status & navigation bar + sDeviceType = DEVICE_TABLET; + } + } + return sDeviceType; + } + + public static boolean isPhone(Context context) { + return getScreenType(context) == DEVICE_PHONE; + } + + public static boolean isHybrid(Context context) { + return getScreenType(context) == DEVICE_HYBRID; + } + + public static boolean isTablet(Context context) { + return getScreenType(context) == DEVICE_TABLET; + } + + public static void switchScreenOff(Context ctx) { + PowerManager pm = (PowerManager) ctx.getSystemService(Context.POWER_SERVICE); + if (pm != null) { + pm.goToSleep(SystemClock.uptimeMillis()); + } + } + + // Check if device has a notch + public static boolean hasNotch(Context context) { + String displayCutout = context.getResources().getString(R.string.config_mainBuiltInDisplayCutout); + boolean maskDisplayCutout = context.getResources().getBoolean(R.bool.config_maskMainBuiltInDisplayCutout); + boolean displayCutoutExists = (!TextUtils.isEmpty(displayCutout) && !maskDisplayCutout); + return displayCutoutExists; + } + + public static void sendKeycode(int keycode) { + long when = SystemClock.uptimeMillis(); + final KeyEvent evDown = new KeyEvent(when, when, KeyEvent.ACTION_DOWN, keycode, 0, + 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, + KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY, + InputDevice.SOURCE_KEYBOARD); + final KeyEvent evUp = KeyEvent.changeAction(evDown, KeyEvent.ACTION_UP); + + final Handler handler = new Handler(Looper.getMainLooper()); + handler.post(new Runnable() { + @Override + public void run() { + InputManager.getInstance().injectInputEvent(evDown, + InputManager.INJECT_INPUT_EVENT_MODE_ASYNC); + } + }); + handler.postDelayed(new Runnable() { + @Override + public void run() { + InputManager.getInstance().injectInputEvent(evUp, + InputManager.INJECT_INPUT_EVENT_MODE_ASYNC); + } + }, 20); + } + + public static ActivityInfo getRunningActivityInfo(Context context) { + final ActivityManager am = (ActivityManager) context + .getSystemService(Context.ACTIVITY_SERVICE); + final PackageManager pm = context.getPackageManager(); + + List tasks = am.getRunningTasks(1); + if (tasks != null && !tasks.isEmpty()) { + ActivityManager.RunningTaskInfo top = tasks.get(0); + try { + return pm.getActivityInfo(top.topActivity, 0); + } catch (PackageManager.NameNotFoundException e) { + } + } + return null; + } + + // Omni Switch Constants + + /** + * Package name of the omnniswitch app + */ + public static final String APP_PACKAGE_NAME = "org.omnirom.omniswitch"; + + /** + * Intent broadcast action for showing the omniswitch overlay + */ + public static final String ACTION_SHOW_OVERLAY = APP_PACKAGE_NAME + ".ACTION_SHOW_OVERLAY"; + + /** + * Intent broadcast action for hiding the omniswitch overlay + */ + public static final String ACTION_HIDE_OVERLAY = APP_PACKAGE_NAME + ".ACTION_HIDE_OVERLAY"; + + /** + * Intent broadcast action for toogle the omniswitch overlay + */ + public static final String ACTION_TOGGLE_OVERLAY = APP_PACKAGE_NAME + ".ACTION_TOGGLE_OVERLAY"; + + /** + * Intent broadcast action for restoring the home stack + */ + public static final String ACTION_RESTORE_HOME_STACK = APP_PACKAGE_NAME + ".ACTION_RESTORE_HOME_STACK"; + + /** + * Intent for launching the omniswitch settings actvity + */ + public static Intent INTENT_LAUNCH_APP = new Intent(Intent.ACTION_MAIN) + .setClassName(APP_PACKAGE_NAME, APP_PACKAGE_NAME + ".SettingsActivity"); + +} diff --git a/core/java/com/android/internal/util/bootleg/ConfigSplitHelper.java b/core/java/com/android/internal/util/bootleg/ConfigSplitHelper.java new file mode 100644 index 000000000000..f4a752128f86 --- /dev/null +++ b/core/java/com/android/internal/util/bootleg/ConfigSplitHelper.java @@ -0,0 +1,98 @@ +/* +* Copyright (C) 2014 SlimRoms 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.bootleg; + +import android.content.Context; +import android.content.pm.PackageManager; +import android.content.res.Resources; +import android.util.Log; + +import java.util.ArrayList; + +public class ConfigSplitHelper { + + private static final String SETTINGS_METADATA_NAME = "com.android.settings"; + + public static ArrayList getActionConfigValues(Context context, String config, + String values, String entries, boolean isShortcut) { + // init vars to fill with them later the config values + int counter = 0; + ArrayList actionConfigList = new ArrayList(); + ActionConfig actionConfig = null; + + PackageManager pm = context.getPackageManager(); + Resources settingsResources = null; + try { + settingsResources = pm.getResourcesForApplication(SETTINGS_METADATA_NAME); + } catch (Exception e) { + Log.e("ConfigSplitHelper", "can't access settings resources",e); + } + + // Split out the config to work with and add to the list + for (String configValue : config.split("\\" + ActionConstants.ACTION_DELIMITER)) { + counter++; + if (counter == 1) { + actionConfig = new ActionConfig(configValue, + AppHelper.getProperSummary(context, pm, settingsResources, + configValue, values, entries), null, null, null); + } + if (counter == 2) { + if (isShortcut) { + actionConfig.setIcon(configValue); + actionConfigList.add(actionConfig); + //reset counter due that shortcut iteration of one action is finished + counter = 0; + } else { + actionConfig.setLongpressAction(configValue); + actionConfig.setLongpressActionDescription( + AppHelper.getProperSummary(context, pm, settingsResources, + configValue, values, entries)); + } + } + if (counter == 3) { + actionConfig.setIcon(configValue); + actionConfigList.add(actionConfig); + //reset counter due that iteration of full config action is finished + counter = 0; + } + } + + return actionConfigList; + } + + public static String setActionConfig( + ArrayList actionConfigs, boolean isShortcut) { + String finalConfig = ""; + ActionConfig actionConfig; + + for (int i = 0; i < actionConfigs.size(); i++) { + if (i != 0) { + finalConfig += ActionConstants.ACTION_DELIMITER; + } + actionConfig = actionConfigs.get(i); + finalConfig += actionConfig.getClickAction() + ActionConstants.ACTION_DELIMITER; + if (!isShortcut) { + finalConfig += actionConfig.getLongpressAction() + + ActionConstants.ACTION_DELIMITER; + } + finalConfig += actionConfig.getIcon(); + } + + return finalConfig; + } + +} diff --git a/core/java/com/android/internal/util/bootleg/Converter.java b/core/java/com/android/internal/util/bootleg/Converter.java new file mode 100644 index 000000000000..34ebaed6ec4f --- /dev/null +++ b/core/java/com/android/internal/util/bootleg/Converter.java @@ -0,0 +1,30 @@ +/* +* Copyright (C) 2014 SlimRoms 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.bootleg; + +import android.content.Context; + +public class Converter { + + public static int dpToPx(Context context, int dp) { + return (int) ((dp * context.getResources().getDisplayMetrics().density) + 0.5); + } + + public static int pxToDp(Context context, int px) { + return (int) ((px / context.getResources().getDisplayMetrics().density) + 0.5); + } + +} diff --git a/core/java/com/android/internal/util/bootleg/DeviceUtils.java b/core/java/com/android/internal/util/bootleg/DeviceUtils.java new file mode 100644 index 000000000000..bf5ccbc0cf85 --- /dev/null +++ b/core/java/com/android/internal/util/bootleg/DeviceUtils.java @@ -0,0 +1,180 @@ +/* +* Copyright (C) 2014 SlimRoms 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.bootleg; + +import android.bluetooth.BluetoothAdapter; +import android.content.ContentResolver; +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.res.Resources; +import android.hardware.display.DisplayManager; +import android.hardware.display.WifiDisplayStatus; +import android.net.ConnectivityManager; +import android.net.Network; +import android.net.NetworkCapabilities; +import android.net.TetheringManager; +import android.nfc.NfcAdapter; +import android.provider.Settings; +import android.os.Vibrator; +import android.telephony.TelephonyManager; +import android.util.DisplayMetrics; +import android.view.DisplayInfo; +import android.view.WindowManager; +import android.util.Log; + +import com.android.internal.telephony.PhoneConstants; + +import java.util.ArrayList; +import java.util.List; + +public class DeviceUtils { + + private static final String SETTINGS_METADATA_NAME = "com.android.settings"; + + // Device types + private static final int DEVICE_PHONE = 0; + private static final int DEVICE_HYBRID = 1; + private static final int DEVICE_TABLET = 2; + + public static boolean deviceSupportsRemoteDisplay(Context ctx) { + DisplayManager dm = (DisplayManager) ctx.getSystemService(Context.DISPLAY_SERVICE); + return (dm.getWifiDisplayStatus().getFeatureState() + != WifiDisplayStatus.FEATURE_STATE_UNAVAILABLE); + } + + public static boolean deviceSupportsUsbTether(Context context) { + TetheringManager tm = (TetheringManager) context.getSystemService(Context.TETHERING_SERVICE); + return (tm.getTetherableUsbRegexs().length != 0); + } + + public static boolean deviceSupportsMobileData(Context context) { + ConnectivityManager cm = + (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); + final Network activeNetwork = cm.getActiveNetwork(); + if (activeNetwork == null) { + return false; + } + final NetworkCapabilities networkCapabilities = + cm.getNetworkCapabilities(activeNetwork); + if (networkCapabilities == null) { + return false; + } + return networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR); + } + + public static boolean deviceSupportsBluetooth() { + return (BluetoothAdapter.getDefaultAdapter() != null); + } + + public static boolean deviceSupportsNfc(Context context) { + return NfcAdapter.getDefaultAdapter(context) != null; + } + + public static boolean deviceSupportsLte(Context context) { + final TelephonyManager tm = + (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); + return (tm.isLteCdmaEvdoGsmWcdmaEnabled()); + // || tm.getLteOnGsmMode() != 0; // add back if when we have support on LP for it + } + + public static boolean deviceSupportsGps(Context context) { + return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LOCATION_GPS); + } + + public static boolean adbEnabled(ContentResolver resolver) { + return (Settings.Global.getInt(resolver, Settings.Global.ADB_ENABLED, 0)) == 1; + } + + public static boolean deviceSupportsVibrator(Context ctx) { + Vibrator vibrator = (Vibrator) ctx.getSystemService(Context.VIBRATOR_SERVICE); + return vibrator.hasVibrator(); + } + + public static boolean deviceSupportsTorch(Context context) { + // Need to be adapted to new torch API + return true; + } + + public static FilteredDeviceFeaturesArray filterUnsupportedDeviceFeatures(Context context, + String[] valuesArray, String[] entriesArray) { + if (valuesArray == null || entriesArray == null || context == null) { + return null; + } + List finalEntries = new ArrayList(); + List finalValues = new ArrayList(); + FilteredDeviceFeaturesArray filteredDeviceFeaturesArray = + new FilteredDeviceFeaturesArray(); + + for (int i = 0; i < valuesArray.length; i++) { + if (isSupportedFeature(context, valuesArray[i])) { + finalEntries.add(entriesArray[i]); + finalValues.add(valuesArray[i]); + } + } + filteredDeviceFeaturesArray.entries = + finalEntries.toArray(new String[finalEntries.size()]); + filteredDeviceFeaturesArray.values = + finalValues.toArray(new String[finalValues.size()]); + return filteredDeviceFeaturesArray; + } + + private static boolean isSupportedFeature(Context context, String action) { + if (action.equals(ActionConstants.ACTION_TORCH) + && !deviceSupportsTorch(context) + || action.equals(ActionConstants.ACTION_VIB) + && !deviceSupportsVibrator(context) + || action.equals(ActionConstants.ACTION_VIB_SILENT) + && !deviceSupportsVibrator(context)) { + return false; + } + return true; + } + + public static class FilteredDeviceFeaturesArray { + public String[] entries; + public String[] values; + } + + private static int getScreenType(Context con) { + WindowManager wm = (WindowManager)con.getSystemService(Context.WINDOW_SERVICE); + DisplayInfo outDisplayInfo = new DisplayInfo(); + wm.getDefaultDisplay().getDisplayInfo(outDisplayInfo); + int shortSize = Math.min(outDisplayInfo.logicalHeight, outDisplayInfo.logicalWidth); + int shortSizeDp = + shortSize * DisplayMetrics.DENSITY_DEFAULT / outDisplayInfo.logicalDensityDpi; + if (shortSizeDp < 600) { + return DEVICE_PHONE; + } else if (shortSizeDp < 720) { + return DEVICE_HYBRID; + } else { + return DEVICE_TABLET; + } + } + + public static boolean isPhone(Context con) { + return getScreenType(con) == DEVICE_PHONE; + } + + public static boolean isHybrid(Context con) { + return getScreenType(con) == DEVICE_HYBRID; + } + + public static boolean isTablet(Context con) { + return getScreenType(con) == DEVICE_TABLET; + } + +} diff --git a/core/java/com/android/internal/util/bootleg/Helpers.java b/core/java/com/android/internal/util/bootleg/Helpers.java new file mode 100644 index 000000000000..13cdf002e168 --- /dev/null +++ b/core/java/com/android/internal/util/bootleg/Helpers.java @@ -0,0 +1,30 @@ +package com.android.internal.util.bootleg; + +import android.app.ActivityManager; +import android.app.ActivityManagerNative; +import android.app.IActivityManager; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; + +public class Helpers { + // avoids hardcoding the tag + private static final String TAG = Thread.currentThread().getStackTrace()[1].getClassName(); + + public static void restartSystemUI(Context context) { + try { + ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); + IActivityManager amn = ActivityManagerNative.getDefault(); + context.stopService(new Intent().setComponent(new ComponentName("com.android.systemui", "com.android.systemui.SystemUIService"))); + am.killBackgroundProcesses("com.android.systemui"); + for (ActivityManager.RunningAppProcessInfo app : am.getRunningAppProcesses()) { + if ("com.android.systemui".equals(app.processName)) { + amn.killApplicationProcess(app.processName, app.uid); + break; + } + } + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/core/java/com/android/internal/util/bootleg/ImageHelper.java b/core/java/com/android/internal/util/bootleg/ImageHelper.java new file mode 100644 index 000000000000..b5994d6c2fab --- /dev/null +++ b/core/java/com/android/internal/util/bootleg/ImageHelper.java @@ -0,0 +1,147 @@ +/* +* Copyright (C) 2013 SlimRoms 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.bootleg; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Bitmap.Config; +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.util.TypedValue; + +public class ImageHelper { + + 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; + } + + private static Bitmap toGrayscale(Bitmap bmpOriginal) { + int width, height; + height = bmpOriginal.getHeight(); + width = bmpOriginal.getWidth(); + + 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 Drawable resize(Context context, Drawable image, int size) { + if (image == null || context == null) { + return null; + } + + int newSize = Converter.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; + } + +} diff --git a/core/java/com/android/internal/util/bootleg/KeyDisabler.java b/core/java/com/android/internal/util/bootleg/KeyDisabler.java new file mode 100644 index 000000000000..8fb09dfb234b --- /dev/null +++ b/core/java/com/android/internal/util/bootleg/KeyDisabler.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2014 The CyanogenMod Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.util.bootleg; + +/* + * Disable capacitive keys + * + * This is intended for use on devices in which the capacitive keys + * can be fully disabled for replacement with a soft navbar. You + * really should not be using this on a device with mechanical or + * otherwise visible-when-inactive keys + */ + +public class KeyDisabler { + + /* + * All HAF classes should export this boolean. + * Real implementations must, of course, return true + */ + + public static boolean isSupported() { return false; } + + /* + * Are the keys currently blocked? + */ + + public static boolean isActive() { + return false; + } + + /* + * Disable capacitive keys + */ + + public static boolean setActive(boolean state) { + throw new UnsupportedOperationException(); + } + +} diff --git a/core/java/com/android/internal/util/bootleg/ThemeUtils.java b/core/java/com/android/internal/util/bootleg/ThemeUtils.java new file mode 100644 index 000000000000..f0cc4d635127 --- /dev/null +++ b/core/java/com/android/internal/util/bootleg/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.bootleg; + +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.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)) { + mOverlayManager.setEnabled(currentPackageName, false, USER_SYSTEM); + } else { + mOverlayManager.setEnabledExclusiveInCategory(packageName, + USER_SYSTEM); + } + + writeSettings(category, packageName, target.equals(packageName)); + + } catch (RemoteException e) { + } + } + + public void writeSettings(String category, String packageName, boolean disable) { + final String overlayPackageJson = Settings.Secure.getStringForUser( + mContext.getContentResolver(), + Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES, USER_SYSTEM); + 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(), USER_SYSTEM); + } 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) { + throw re.rethrowFromSystemServer(); + } + filteredInfos.sort(OVERLAY_INFO_COMPARATOR); + return filteredInfos; + } + + public List getFonts() { + final List fontlist = new ArrayList<>(); + for (String overlayPackage : getOverlayPackagesForCategory(FONT_KEY)) { + try { + overlayRes = overlayPackage.equals("android") ? Resources.getSystem() + : pm.getResourcesForApplication(overlayPackage); + final String font = overlayRes.getString( + overlayRes.getIdentifier("config_bodyFontFamily", + "string", overlayPackage)); + fontlist.add(Typeface.create(font, Typeface.NORMAL)); + } catch (NameNotFoundException | NotFoundException e) { + // Do nothing + } + } + 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 + } + 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) { + e.printStackTrace(); + } + return false; + } + + public boolean isDefaultOverlay(String category) { + for (String overlayPackage : getOverlayPackagesForCategory(category)) { + try { + OverlayInfo info = mOverlayManager.getOverlayInfo(overlayPackage, USER_SYSTEM); + if (info != null && info.isEnabled()) { + return false; + } else { + continue; + } + } catch (RemoteException e) { + e.printStackTrace(); + } + } + return true; + } +} diff --git a/core/java/com/android/internal/util/custom/PixelPropsUtils.java b/core/java/com/android/internal/util/custom/PixelPropsUtils.java new file mode 100644 index 000000000000..3d566fc2c6df --- /dev/null +++ b/core/java/com/android/internal/util/custom/PixelPropsUtils.java @@ -0,0 +1,411 @@ +/* + * Copyright (C) 2020 The Pixel Experience Project + * 2021-2022 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.custom; + +import android.app.Application; +import android.os.Build; +import android.os.SystemProperties; +import android.util.Log; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +public class PixelPropsUtils { + + private static final String TAG = PixelPropsUtils.class.getSimpleName(); + private static final String DEVICE = "ro.product.device"; + private static final boolean DEBUG = false; + + private static final Map propsToChange; + private static final Map propsToChangePixel5; + private static final Map propsToChangePixel7Pro; + private static final Map propsToChangePixelXL; + private static final Map propsToChangeROG1; + private static final Map propsToChangeROG3; + private static final Map propsToChangeXP5; + private static final Map propsToChangeOP8P; + private static final Map propsToChangeOP9R; + private static final Map propsToChange11T; + private static final Map propsToChangeF4; + private static final Map> propsToKeep; + + // Packages to Spoof as Pixel 7 Pro + private static final String[] packagesToChangePixel7Pro = { + "com.google.android.apps.wallpaper", + "com.google.android.apps.privacy.wildlife", + "com.google.android.apps.subscriptions.red", + "com.google.android.inputmethod.latin" + }; + + // Packages to Spoof as Pixel XL + private static final String[] packagesToChangePixelXL = { + "com.samsung.accessory", + "com.samsung.accessory.fridaymgr", + "com.samsung.accessory.berrymgr", + "com.samsung.accessory.neobeanmgr", + "com.samsung.android.app.watchmanager", + "com.samsung.android.geargplugin", + "com.samsung.android.gearnplugin", + "com.samsung.android.modenplugin", + "com.samsung.android.neatplugin", + "com.samsung.android.waterplugin" + }; + + // Packages to Spoof as Pixel 7 Pro + private static final String[] extraPackagesToChange = { + "com.android.chrome", + "com.android.vending", + "com.breel.wallpapers20", + "com.nhs.online.nhsonline", + "com.netflix.mediaclient" + }; + + // Packages to Keep with original device + private static final String[] packagesToKeep = { + "com.google.android.GoogleCamera", + "com.google.android.GoogleCamera.Cameight", + "com.google.android.GoogleCamera.Go", + "com.google.android.GoogleCamera.Urnyx", + "com.google.android.GoogleCameraAsp", + "com.google.android.GoogleCameraCVM", + "com.google.android.GoogleCameraEng", + "com.google.android.GoogleCameraEng2", + "com.google.android.GoogleCameraGood", + "com.google.android.MTCL83", + "com.google.android.UltraCVM", + "com.google.android.apps.cameralite", + "com.google.android.dialer", + "com.google.android.euicc", + "com.google.ar.core", + "com.google.android.youtube", + "com.google.android.apps.youtube.kids", + "com.google.android.apps.youtube.music", + "com.google.android.apps.recorder", + "com.google.android.apps.wearables.maestro.companion" + }; + + // Packages to Spoof as ROG Phone 1 + private static final String[] packagesToChangeROG1 = { + "com.madfingergames.legends" + }; + + // Packages to Spoof as ROG Phone 3 + private static final String[] packagesToChangeROG3 = { + "com.pearlabyss.blackdesertm", + "com.pearlabyss.blackdesertm.gl" + }; + + // Packages to Spoof as Xperia 5 + private static final String[] packagesToChangeXP5 = { + "com.activision.callofduty.shooter", + "com.garena.game.codm", + "com.tencent.tmgp.kr.codm", + "com.vng.codmvn" + }; + + // Packages to Spoof as OnePlus 8 Pro + private static final String[] packagesToChangeOP8P = { + "com.netease.lztgglobal", + "com.pubg.imobile", + "com.pubg.krmobile", + "com.rekoo.pubgm", + "com.riotgames.league.wildrift", + "com.riotgames.league.wildrifttw", + "com.riotgames.league.wildriftvn", + "com.tencent.ig", + "com.tencent.tmgp.pubgmhd", + "com.vng.pubgmobile" + }; + + // Packages to Spoof as OnePlus 9R + private static final String[] packagesToChangeOP9R = { + "com.epicgames.fortnite", + "com.epicgames.portal" + }; + + // Packages to Spoof as Mi 11T + private static final String[] packagesToChange11T = { + "com.ea.gp.apexlegendsmobilefps", + "com.levelinfinite.hotta.gp", + "com.mobile.legends", + "com.supercell.clashofclans", + "com.tencent.tmgp.sgame", + "com.vng.mlbbvn" + }; + + // Packages to Spoof as POCO F4 + private static final String[] packagesToChangeF4 = { + "com.dts.freefiremax", + "com.dts.freefireth" + }; + + // Codenames for currently supported Pixels by Google + private static final String[] pixelCodenames = { + "cheetah", + "panther", + "bluejay", + "oriole", + "raven", + "barbet", + "redfin", + "bramble", + "sunfish", + "coral", + "flame" + }; + + private static volatile boolean sIsGms = false; + private static volatile boolean sIsFinsky = false; + + static { + propsToKeep = new HashMap<>(); + propsToChange = new HashMap<>(); + propsToKeep.put("com.google.android.settings.intelligence", new ArrayList<>(Collections.singletonList("FINGERPRINT"))); + propsToChangePixel7Pro = new HashMap<>(); + propsToChangePixel7Pro.put("BRAND", "google"); + propsToChangePixel7Pro.put("MANUFACTURER", "Google"); + propsToChangePixel7Pro.put("DEVICE", "cheetah"); + propsToChangePixel7Pro.put("PRODUCT", "cheetah"); + propsToChangePixel7Pro.put("MODEL", "Pixel 7 Pro"); + propsToChangePixel7Pro.put("FINGERPRINT", "google/cheetah/cheetah:13/TQ1A.221205.011/9244662:user/release-keys"); + propsToChangePixel5 = new HashMap<>(); + propsToChangePixel5.put("BRAND", "google"); + propsToChangePixel5.put("MANUFACTURER", "Google"); + propsToChangePixel5.put("DEVICE", "redfin"); + propsToChangePixel5.put("PRODUCT", "redfin"); + propsToChangePixel5.put("MODEL", "Pixel 5"); + propsToChangePixel5.put("FINGERPRINT", "google/redfin/redfin:13/TQ1A.221205.011/9244662:user/release-keys"); + propsToChangePixelXL = new HashMap<>(); + propsToChangePixelXL.put("BRAND", "google"); + propsToChangePixelXL.put("MANUFACTURER", "Google"); + propsToChangePixelXL.put("DEVICE", "marlin"); + propsToChangePixelXL.put("PRODUCT", "marlin"); + propsToChangePixelXL.put("MODEL", "Pixel XL"); + propsToChangePixelXL.put("FINGERPRINT", "google/marlin/marlin:10/QP1A.191005.007.A3/5972272:user/release-keys"); + propsToChangeROG1 = new HashMap<>(); + propsToChangeROG1.put("MODEL", "ASUS_Z01QD"); + propsToChangeROG1.put("MANUFACTURER", "asus"); + propsToChangeROG3 = new HashMap<>(); + propsToChangeROG3.put("MODEL", "ASUS_I003D"); + propsToChangeROG3.put("MANUFACTURER", "asus"); + propsToChangeXP5 = new HashMap<>(); + propsToChangeXP5.put("MODEL", "SO-52A"); + propsToChangeXP5.put("MANUFACTURER", "Sony"); + propsToChangeOP8P = new HashMap<>(); + propsToChangeOP8P.put("MODEL", "IN2020"); + propsToChangeOP8P.put("MANUFACTURER", "OnePlus"); + propsToChangeOP9R = new HashMap<>(); + propsToChangeOP9R.put("MODEL", "LE2101"); + propsToChangeOP9R.put("MANUFACTURER", "OnePlus"); + propsToChange11T = new HashMap<>(); + propsToChange11T.put("MODEL", "21081111RG"); + propsToChange11T.put("MANUFACTURER", "Xiaomi"); + propsToChangeF4 = new HashMap<>(); + propsToChangeF4.put("MODEL", "22021211RG"); + propsToChangeF4.put("MANUFACTURER", "Xiaomi"); + } + + public static void setProps(String packageName) { + if (packageName == null || packageName.isEmpty()) { + return; + } + if (Arrays.asList(packagesToKeep).contains(packageName)) { + return; + } + if (packageName.startsWith("com.google.") + || Arrays.asList(extraPackagesToChange).contains(packageName)) { + + boolean isPixelDevice = Arrays.asList(pixelCodenames).contains(SystemProperties.get(DEVICE)); + + if (packageName.equals("com.google.android.apps.photos")) { + if (SystemProperties.getBoolean("persist.sys.pixelprops.gphotos", true)) { + propsToChange.putAll(propsToChangePixelXL); + } else { + if (isPixelDevice) return; + propsToChange.putAll(propsToChangePixel5); + } + } else if (packageName.equals("com.netflix.mediaclient") && + !SystemProperties.getBoolean("persist.sys.pixelprops.netflix", false)) { + if (DEBUG) Log.d(TAG, "Netflix spoofing disabled by system prop"); + return; + } else if (isPixelDevice) { + return; + } else if (packageName.equals("com.android.vending")) { + sIsFinsky = true; + return; + } else { + if (Arrays.asList(packagesToChangePixel7Pro).contains(packageName)) { + propsToChange.putAll(propsToChangePixel7Pro); + } else if (Arrays.asList(packagesToChangePixelXL).contains(packageName)) { + propsToChange.putAll(propsToChangePixelXL); + } else { + propsToChange.putAll(propsToChangePixel5); + } + } + + if (DEBUG) Log.d(TAG, "Defining props for: " + packageName); + for (Map.Entry prop : propsToChange.entrySet()) { + String key = prop.getKey(); + Object value = prop.getValue(); + if (propsToKeep.containsKey(packageName) && propsToKeep.get(packageName).contains(key)) { + if (DEBUG) Log.d(TAG, "Not defining " + key + " prop for: " + packageName); + continue; + } + if (DEBUG) Log.d(TAG, "Defining " + key + " prop for: " + packageName); + setPropValue(key, value); + } + if (packageName.equals("com.google.android.gms")) { + final String processName = Application.getProcessName(); + if (processName.equals("com.google.android.gms.unstable")) { + sIsGms = true; + spoofBuildGms(); + } + return; + } + // Set proper indexing fingerprint + if (packageName.equals("com.google.android.settings.intelligence")) { + setPropValue("FINGERPRINT", Build.VERSION.INCREMENTAL); + } + } else { + + if (!SystemProperties.getBoolean("persist.sys.pixelprops.games", false)) + return; + + if (Arrays.asList(packagesToChangeROG1).contains(packageName)) { + if (DEBUG) Log.d(TAG, "Defining props for: " + packageName); + for (Map.Entry prop : propsToChangeROG1.entrySet()) { + String key = prop.getKey(); + Object value = prop.getValue(); + setPropValue(key, value); + } + } else if (Arrays.asList(packagesToChangeROG3).contains(packageName)) { + if (DEBUG) Log.d(TAG, "Defining props for: " + packageName); + for (Map.Entry prop : propsToChangeROG3.entrySet()) { + String key = prop.getKey(); + Object value = prop.getValue(); + setPropValue(key, value); + } + } else if (Arrays.asList(packagesToChangeXP5).contains(packageName)) { + if (DEBUG) Log.d(TAG, "Defining props for: " + packageName); + for (Map.Entry prop : propsToChangeXP5.entrySet()) { + String key = prop.getKey(); + Object value = prop.getValue(); + setPropValue(key, value); + } + } else if (Arrays.asList(packagesToChangeOP8P).contains(packageName)) { + if (DEBUG) Log.d(TAG, "Defining props for: " + packageName); + for (Map.Entry prop : propsToChangeOP8P.entrySet()) { + String key = prop.getKey(); + Object value = prop.getValue(); + setPropValue(key, value); + } + } else if (Arrays.asList(packagesToChangeOP9R).contains(packageName)) { + if (DEBUG) Log.d(TAG, "Defining props for: " + packageName); + for (Map.Entry prop : propsToChangeOP9R.entrySet()) { + String key = prop.getKey(); + Object value = prop.getValue(); + setPropValue(key, value); + } + } else if (Arrays.asList(packagesToChange11T).contains(packageName)) { + if (DEBUG) Log.d(TAG, "Defining props for: " + packageName); + for (Map.Entry prop : propsToChange11T.entrySet()) { + String key = prop.getKey(); + Object value = prop.getValue(); + setPropValue(key, value); + } + } else if (Arrays.asList(packagesToChangeF4).contains(packageName)) { + if (DEBUG) Log.d(TAG, "Defining props for: " + packageName); + for (Map.Entry prop : propsToChangeF4.entrySet()) { + String key = prop.getKey(); + Object value = prop.getValue(); + setPropValue(key, value); + } + } + } + } + + private static void setPropValue(String key, Object value) { + try { + if (DEBUG) Log.d(TAG, "Defining prop " + key + " to " + value.toString()); + Field field = Build.class.getDeclaredField(key); + field.setAccessible(true); + field.set(null, value); + field.setAccessible(false); + } catch (NoSuchFieldException | IllegalAccessException e) { + Log.e(TAG, "Failed to set prop " + key, e); + } + } + + private static void setBuildField(String key, String value) { + try { + // Unlock + Field field = Build.class.getDeclaredField(key); + field.setAccessible(true); + + // Edit + field.set(null, value); + + // Lock + field.setAccessible(false); + } catch (NoSuchFieldException | IllegalAccessException e) { + Log.e(TAG, "Failed to spoof Build." + key, e); + } + } + + private static void setVersionField(String key, Integer value) { + try { + // Unlock + Field field = Build.VERSION.class.getDeclaredField(key); + field.setAccessible(true); + + // Edit + field.set(null, value); + + // Lock + field.setAccessible(false); + } catch (NoSuchFieldException | IllegalAccessException e) { + Log.e(TAG, "Failed to spoof Build." + key, e); + } + } + + private static void spoofBuildGms() { + // Alter model name and fingerprint to avoid hardware attestation enforcement + setBuildField("FINGERPRINT", "google/marlin/marlin:7.1.2/NJH47F/4146041:user/release-keys"); + setBuildField("PRODUCT", "marlin"); + setBuildField("DEVICE", "marlin"); + setBuildField("MODEL", "Pixel XL"); + setVersionField("DEVICE_INITIAL_SDK_INT", Build.VERSION_CODES.N_MR1); + } + + private static boolean isCallerSafetyNet() { + return sIsGms && Arrays.stream(Thread.currentThread().getStackTrace()) + .anyMatch(elem -> elem.getClassName().contains("DroidGuard")); + } + + public static void onEngineGetCertificateChain() { + // Check stack for SafetyNet or Play Integrity + if (isCallerSafetyNet() || sIsFinsky) { + Log.i(TAG, "Blocked key attestation sIsGms=" + sIsGms + " sIsFinsky=" + sIsFinsky); + throw new UnsupportedOperationException(); + } + } +} diff --git a/core/java/com/android/internal/util/omni/OmniJawsClient.java b/core/java/com/android/internal/util/omni/OmniJawsClient.java new file mode 100644 index 000000000000..24883093eb39 --- /dev/null +++ b/core/java/com/android/internal/util/omni/OmniJawsClient.java @@ -0,0 +1,560 @@ +/* +* Copyright (C) 2017 The OmniROM Project +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* +*/ +package com.android.internal.util.omni; + +import java.text.DecimalFormat; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +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.ContentObserver; +import android.database.Cursor; +import android.graphics.Color; +import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.os.Handler; +import android.os.UserHandle; +import android.provider.Settings; +import android.util.Log; + +public class OmniJawsClient { + private static final String TAG = "SystemUI: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"); + + private static final String ICON_PACKAGE_DEFAULT = "org.omnirom.omnijaws"; + private static final String ICON_PREFIX_DEFAULT = "outline"; + 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 = new String[]{ + "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" + }; + + final String[] SETTINGS_PROJECTION = new String[] { + "enabled", + "units", + "provider", + "setup" + }; + + private static final String WEATHER_UPDATE = "org.omnirom.omnijaws.WEATHER_UPDATE"; + private static final String WEATHER_ERROR = "org.omnirom.omnijaws.WEATHER_ERROR"; + + private static final String SETTINGS_PACKAGE_NAME = "com.android.settings"; + private static final String WEATHER_SETTINGS = "com.android.settings.Settings$OmniJawsSettingsActivity"; + private static final String EXTRA_SHOW_FRAGMENT = ":android:show_fragment"; + + private static final DecimalFormat sNoDigitsFormat = new DecimalFormat("0"); + + 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 toString() { + return city + ":" + new Date(timeStamp) + ": " + windSpeed + ":" + windDirection + ":" +conditionCode + ":" + temp + ":" + humidity + ":" + condition + ":" + tempUnits + ":" + windUnits + ": " + forecasts; + } + + public String getLastUpdateTime() { + SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss"); + return sdf.format(new Date(timeStamp)); + } + } + + public static class DayForecast { + public String low; + public String high; + public int conditionCode; + public String condition; + public String date; + + public String toString() { + return "[" + low + ":" + high + ":" +conditionCode + ":" + condition + ":" + date + "]"; + } + } + + public static class PackageInfo { + public String packageName; + public int resourceID; + + public String toString() { + return "[" + packageName + ":" + resourceID + "]"; + } + } + + public static interface OmniJawsObserver { + public void weatherUpdated(); + public void weatherError(int errorReason); + default public void updateSettings() {}; + } + + private class WeatherUpdateReceiver extends BroadcastReceiver { + @Override + public void onReceive(final Context context, Intent intent) { + String action = intent.getAction(); + for (OmniJawsObserver observer : mObserver) { + if (action.equals(WEATHER_UPDATE)) { + observer.weatherUpdated(); + } + if (action.equals(WEATHER_ERROR)) { + int errorReason = intent.getIntExtra(EXTRA_ERROR, 0); + observer.weatherError(errorReason); + } + } + } + } + + private class OmniJawsSettingsObserver extends ContentObserver { + OmniJawsSettingsObserver(Handler handler) { + super(handler); + } + + void observe() { + mContext.getContentResolver().registerContentObserver(Settings.System.getUriFor( + Settings.System.OMNIJAWS_WEATHER_ICON_PACK), + false, this, UserHandle.USER_ALL); + update(); + } + + @Override + public void onChange(boolean selfChange) { + update(); + } + + public void update() { + updateSettings(); + } + + public void unregister() { + mContext.getContentResolver().unregisterContentObserver(this); + } + } + + private Context mContext; + private WeatherInfo mCachedInfo; + private PackageInfo mImageInfo; + private Resources mRes; + private String mPackageName; + private String mIconPrefix; + private String mSettingIconPackage; + private boolean mMetric; + private List mObserver; + private WeatherUpdateReceiver mReceiver; + private OmniJawsSettingsObserver mSettingsObserver; + private Handler mHandler = new Handler(); + + public OmniJawsClient(Context context) { + mContext = context; + mObserver = new ArrayList(); + mSettingsObserver = new OmniJawsSettingsObserver(mHandler); + mSettingsObserver.observe(); + } + + public OmniJawsClient(Context context, boolean settingsObserver) { + mContext = context; + mObserver = new ArrayList(); + if (settingsObserver) { + mSettingsObserver = new OmniJawsSettingsObserver(mHandler); + mSettingsObserver.observe(); + } + } + + public void addSettingsObserver() { + if (mSettingsObserver == null) { + mSettingsObserver = new OmniJawsSettingsObserver(mHandler); + mSettingsObserver.observe(); + } + } + + public void cleanupObserver() { + if (mReceiver != null) { + try { + mContext.unregisterReceiver(mReceiver); + } catch (Exception e) { + } + mReceiver = null; + } + if (mSettingsObserver != null) { + mSettingsObserver.unregister(); + } + } + + public void updateWeather() { + if (isOmniJawsServiceInstalled()) { + Intent updateIntent = new Intent(Intent.ACTION_MAIN) + .setClassName(SERVICE_PACKAGE, SERVICE_PACKAGE + ".WeatherService"); + updateIntent.setAction(SERVICE_PACKAGE + ".ACTION_UPDATE"); + mContext.startService(updateIntent); + } + } + + public Intent getSettingsIntent() { + if (isOmniJawsServiceInstalled()) { + Intent settings = new Intent(Intent.ACTION_MAIN); + settings.setClassName(SETTINGS_PACKAGE_NAME, WEATHER_SETTINGS); + settings.putExtra(EXTRA_SHOW_FRAGMENT, WEATHER_SETTINGS); + return settings; + } + return null; + } + + public WeatherInfo getWeatherInfo() { + return mCachedInfo; + } + + public PackageInfo getPackageInfo() { + return mImageInfo; + } + + private static String getFormattedValue(float value) { + if (Float.isNaN(value)) { + return "-"; + } + String formatted = sNoDigitsFormat.format(value); + if (formatted.equals("-0")) { + formatted = "0"; + } + return formatted; + } + + public void queryWeather() { + if (!isOmniJawsEnabled()) { + Log.w(TAG, "queryWeather while disabled"); + mCachedInfo = null; + return; + } + Cursor c = mContext.getContentResolver().query(WEATHER_URI, WEATHER_PROJECTION, + null, null, null); + mCachedInfo = null; + if (c != null) { + try { + int count = c.getCount(); + if (count > 0) { + mCachedInfo = new WeatherInfo(); + List forecastList = new ArrayList(); + int i = 0; + for (i = 0; i < count; i++) { + c.moveToPosition(i); + if (i == 0) { + mCachedInfo.city = c.getString(0); + mCachedInfo.windSpeed = getFormattedValue(c.getFloat(1)); + mCachedInfo.windDirection = String.valueOf(c.getInt(2)) + "\u00b0"; + mCachedInfo.conditionCode = c.getInt(3); + mCachedInfo.temp = getFormattedValue(c.getFloat(4)); + mCachedInfo.humidity = c.getString(5); + mCachedInfo.condition = c.getString(6); + mCachedInfo.timeStamp = Long.valueOf(c.getString(11)); + mCachedInfo.pinWheel = c.getString(13); + } else { + DayForecast day = new DayForecast(); + day.low = getFormattedValue(c.getFloat(7)); + day.high = getFormattedValue(c.getFloat(8)); + day.condition = c.getString(9); + day.conditionCode = c.getInt(10); + day.date = c.getString(12); + forecastList.add(day); + } + } + mCachedInfo.forecasts = forecastList; + } + } finally { + c.close(); + } + } + updateUnits(); + if (DEBUG) Log.d(TAG, "queryWeather " + mCachedInfo); + } + + private void loadDefaultIconsPackage() { + mPackageName = ICON_PACKAGE_DEFAULT; + mIconPrefix = ICON_PREFIX_DEFAULT; + mSettingIconPackage = mPackageName + "." + mIconPrefix; + if (DEBUG) Log.d(TAG, "Load default icon pack " + mSettingIconPackage + " " + mPackageName + " " + mIconPrefix); + try { + PackageManager packageManager = mContext.getPackageManager(); + mRes = packageManager.getResourcesForApplication(mPackageName); + } catch (Exception e) { + mRes = null; + } + if (mRes == null) { + Log.w(TAG, "No default package found"); + } + } + + private Drawable getDefaultConditionImage() { + String packageName = ICON_PACKAGE_DEFAULT; + String iconPrefix = ICON_PREFIX_DEFAULT; + + try { + PackageManager packageManager = mContext.getPackageManager(); + Resources res = packageManager.getResourcesForApplication(packageName); + if (res != null) { + int resId = res.getIdentifier(iconPrefix + "_na", "drawable", packageName); + Drawable d = res.getDrawable(resId); + if (d != null) { + setWeatherConditionImageResources(resId, packageName); + return d; + } + } + } catch (Exception e) { + } + // absolute absolute fallback + Log.w(TAG, "No default package found"); + return new ColorDrawable(Color.RED); + } + + private void loadCustomIconPackage() { + int idx = mSettingIconPackage.lastIndexOf("."); + mPackageName = mSettingIconPackage.substring(0, idx); + mIconPrefix = mSettingIconPackage.substring(idx + 1); + if (DEBUG) Log.d(TAG, "Load custom icon pack " + mSettingIconPackage + " " + mPackageName + " " + mIconPrefix); + try { + PackageManager packageManager = mContext.getPackageManager(); + mRes = packageManager.getResourcesForApplication(mPackageName); + } catch (Exception e) { + mRes = null; + } + if (mRes == null) { + Log.w(TAG, "Icon pack loading failed - loading default"); + loadDefaultIconsPackage(); + } + } + + private void setWeatherConditionImageResources(int resId, String PackageName) { + if (DEBUG) { + Log.d(TAG, "Setting mImageInfo.resourceID:" + resId); + Log.d(TAG, "Setting mImageInfo.packageName:" + PackageName); + } + mImageInfo.packageName = PackageName; + mImageInfo.resourceID = resId; + } + + public Drawable getWeatherConditionImage(int conditionCode) { + /* Wrapper function allowing to load icon from default package defined as ICON_PACKAGE_DEFAULT, + for instance in the weather QS tile where a colored icon rendered improperly */ + + return getWeatherConditionImagefromPack(conditionCode, /* Don't force default pack*/ false); + } + + public Drawable getWeatherConditionImagefromPack(int conditionCode, boolean defaultPack) { + if (!isOmniJawsEnabled()) { + Log.w(TAG, "Requesting condition image while disabled"); + return null; + } + if (!isAvailableApp(mPackageName) || defaultPack) { + Log.w(TAG, "Icon pack no longer available - loading default " + mPackageName); + loadDefaultIconsPackage(); + } + if (mRes == null) { + Log.w(TAG, "Requesting condition image while disabled"); + return null; + } + try { + mImageInfo = new PackageInfo(); + int resId = mRes.getIdentifier(mIconPrefix + "_" + conditionCode, "drawable", mPackageName); + Drawable d = mRes.getDrawable(resId); + if (d != null) { + setWeatherConditionImageResources(resId, mPackageName); + return d; + } + Log.w(TAG, "Failed to get condition image for " + conditionCode + " use default"); + resId = mRes.getIdentifier(mIconPrefix + "_na", "drawable", mPackageName); + d = mRes.getDrawable(resId); + if (d != null) { + setWeatherConditionImageResources(resId, mPackageName); + return d; + } + } catch(Exception e) { + } + Log.w(TAG, "Failed to get condition image for " + conditionCode); + return getDefaultConditionImage(); + } + + public boolean isOmniJawsServiceInstalled() { + return isAvailableApp(SERVICE_PACKAGE); + } + + public boolean isOmniJawsEnabled() { + if (!isOmniJawsServiceInstalled()) { + return false; + } + final Cursor c = mContext.getContentResolver().query(SETTINGS_URI, SETTINGS_PROJECTION, + null, null, null); + if (c != null) { + int count = c.getCount(); + if (count == 1) { + c.moveToPosition(0); + boolean enabled = c.getInt(0) == 1; + return enabled; + } + } + return true; + } + + public void setOmniJawsEnabled(boolean value) { + if (isOmniJawsServiceInstalled()) { + // check first time enablement and redirect to settings + // cause we need to enable gps for it + Intent updateIntent = new Intent(Intent.ACTION_MAIN) + .setClassName(SERVICE_PACKAGE, SERVICE_PACKAGE + ".WeatherService"); + updateIntent.setAction(SERVICE_PACKAGE + ".ACTION_ENABLE"); + updateIntent.putExtra("enable", value); + mContext.startService(updateIntent); + } + } + + public boolean isOmniJawsSetupDone() { + if (!isOmniJawsServiceInstalled()) { + return false; + } + final Cursor c = mContext.getContentResolver().query(SETTINGS_URI, SETTINGS_PROJECTION, + null, null, null); + if (c != null) { + int count = c.getCount(); + if (count == 1) { + c.moveToPosition(0); + boolean setupDone = c.getInt(3) == 1; + return setupDone; + } + } + return true; + } + + private void updateUnits() { + if (!isOmniJawsServiceInstalled()) { + return; + } + final Cursor c = mContext.getContentResolver().query(SETTINGS_URI, SETTINGS_PROJECTION, + null, null, null); + if (c != null) { + int count = c.getCount(); + if (count == 1) { + c.moveToPosition(0); + mMetric = c.getInt(1) == 0; + if (mCachedInfo != null) { + mCachedInfo.tempUnits = getTemperatureUnit(); + mCachedInfo.windUnits = getWindUnit(); + mCachedInfo.provider = c.getString(2); + } + } + } + } + + private String getTemperatureUnit() { + return "\u00b0" + (mMetric ? "C" : "F"); + } + + private String getWindUnit() { + return mMetric ? "km/h":"mph"; + } + + private void updateSettings() { + if (isOmniJawsServiceInstalled()) { + final String iconPack = Settings.System.getStringForUser(mContext.getContentResolver(), + Settings.System.OMNIJAWS_WEATHER_ICON_PACK, UserHandle.USER_CURRENT); + if (iconPack == null) { + loadDefaultIconsPackage(); + } else if (mSettingIconPackage == null || !iconPack.equals(mSettingIconPackage)) { + mSettingIconPackage = iconPack; + loadCustomIconPackage(); + } + for (OmniJawsObserver observer : mObserver) { + observer.updateSettings(); + } + } + } + + private boolean isAvailableApp(String packageName) { + final PackageManager pm = mContext.getPackageManager(); + try { + pm.getPackageInfo(packageName, PackageManager.GET_ACTIVITIES); + int enabled = pm.getApplicationEnabledSetting(packageName); + return enabled != PackageManager.COMPONENT_ENABLED_STATE_DISABLED && + enabled != PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER; + } catch (NameNotFoundException e) { + return false; + } + } + + public void addObserver(OmniJawsObserver observer) { + if (mObserver.size() == 0) { + if (mReceiver != null) { + try { + mContext.unregisterReceiver(mReceiver); + } catch (Exception e) { + } + } + mReceiver = new WeatherUpdateReceiver(); + IntentFilter filter = new IntentFilter(); + filter.addAction(WEATHER_UPDATE); + filter.addAction(WEATHER_ERROR); + mContext.registerReceiver(mReceiver, filter); + } + mObserver.add(observer); + } + + public void removeObserver(OmniJawsObserver observer) { + mObserver.remove(observer); + if (mObserver.size() == 0 && mReceiver != null) { + try { + mContext.unregisterReceiver(mReceiver); + } catch (Exception e) { + } + mReceiver = null; + } + } +} diff --git a/core/java/com/android/internal/view/BaseSurfaceHolder.java b/core/java/com/android/internal/view/BaseSurfaceHolder.java index 32ce0fe1282b..1ae1307633bb 100644 --- a/core/java/com/android/internal/view/BaseSurfaceHolder.java +++ b/core/java/com/android/internal/view/BaseSurfaceHolder.java @@ -241,4 +241,4 @@ public void setSurfaceFrameSize(int width, int height) { mSurfaceFrame.right = width; mSurfaceFrame.bottom = height; } -}; +} diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java index a4da8de44880..73af16c250cc 100644 --- a/core/java/com/android/internal/widget/LockPatternUtils.java +++ b/core/java/com/android/internal/widget/LockPatternUtils.java @@ -80,6 +80,11 @@ public class LockPatternUtils { */ public static final String LEGACY_LOCK_PATTERN_ENABLED = "legacy_lock_pattern_enabled"; + /** + * The key to store PIN/Password length for quick unlock. + **/ + public static final String KEY_PIN_PASSWORD_LENGTH = "pin_password_length"; + /** * The interval of the countdown for showing progress of the lockout. */ @@ -1522,8 +1527,7 @@ public static class StrongAuthTracker { STRONG_AUTH_REQUIRED_AFTER_LOCKOUT, STRONG_AUTH_REQUIRED_AFTER_TIMEOUT, STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN, - STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT, - SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED}) + STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT}) @Retention(RetentionPolicy.SOURCE) public @interface StrongAuthFlags {} @@ -1575,12 +1579,6 @@ public static class StrongAuthTracker { */ public static final int STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT = 0x80; - /** - * Some authentication is required because the trustagent either timed out or was disabled - * manually. - */ - public static final int SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED = 0x100; - /** * Strong auth flags that do not prevent biometric methods from being accepted as auth. * If any other flags are set, biometric authentication is disabled. @@ -1812,4 +1810,23 @@ public void removeCachedUnifiedChallenge(int userId) { re.rethrowFromSystemServer(); } } + + public int getPinPasswordLength(int userId) { + int mPinPasswordLength = -1; + try { + mPinPasswordLength = (int) getLockSettings().getLong(KEY_PIN_PASSWORD_LENGTH, -1, + userId); + } catch (Exception e) { + Log.d("getPinPasswordLength", "getLong error: " + e.getMessage()); + } + return mPinPasswordLength; + } + + public void setPinPasswordLength(int length, int userId) { + try { + getLockSettings().setLong(KEY_PIN_PASSWORD_LENGTH, (long) length, userId); + } catch (Exception e) { + Log.d("savePinPasswordLength", "saveLong error: " + e.getMessage()); + } + } } diff --git a/core/java/com/android/server/LocalServices.java b/core/java/com/android/server/LocalServices.java index 9c632ea725a9..ca94bb08afc0 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 @@ -32,8 +32,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/ink/kaleidoscope/IParallelSpaceManager.aidl b/core/java/ink/kaleidoscope/IParallelSpaceManager.aidl new file mode 100644 index 000000000000..ae3d8fdaf3c2 --- /dev/null +++ b/core/java/ink/kaleidoscope/IParallelSpaceManager.aidl @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2022 Project Kaleidoscope + * + * 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 ink.kaleidoscope; + +import android.content.pm.UserInfo; + +/** @hide */ +interface IParallelSpaceManager { + int create(String name, boolean shareMedia); + int remove(int userId); + UserInfo[] getUsers(); + UserInfo getOwner(); + int duplicatePackage(String packageName, int userId); + int removePackage(String packageName, int userId); +} diff --git a/core/java/ink/kaleidoscope/ParallelSpaceManager.java b/core/java/ink/kaleidoscope/ParallelSpaceManager.java new file mode 100644 index 000000000000..41938b2550c9 --- /dev/null +++ b/core/java/ink/kaleidoscope/ParallelSpaceManager.java @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2022 Project Kaleidoscope + * + * 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 ink.kaleidoscope; + +import android.content.Context; +import android.content.pm.UserInfo; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.UserHandle; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import ink.kaleidoscope.IParallelSpaceManager; + +/** + * Simple wrapper for ParallelSpaceManagerService. + * It requires android.permission.MANAGE_PARALLEL_SPACES. + * @hide + */ +public final class ParallelSpaceManager { + + public static final String SERVICE_NAME = "parallel"; + private static final String TAG = "ParallelSpaceManager"; + + private static ParallelSpaceManager sParallelSpaceManager; + private IParallelSpaceManager mParallelSpaceManager; + + private ParallelSpaceManager() { + mParallelSpaceManager = IParallelSpaceManager.Stub.asInterface( + ServiceManager.getService(SERVICE_NAME)); + if (mParallelSpaceManager == null) + throw new RuntimeException("Unable to get ParallelSpaceManagerService."); + } + + public static ParallelSpaceManager getInstance() { + if (sParallelSpaceManager != null) + return sParallelSpaceManager; + sParallelSpaceManager = new ParallelSpaceManager(); + return sParallelSpaceManager; + } + + private int create(String name, boolean shareMedia) { + try { + return mParallelSpaceManager.create(name, shareMedia); + } catch (RemoteException e) { + throw new RuntimeException("Failed when create(): " + e); + } + } + + /** + * Create a parallel space. Returns user id on success, -1 otherwise. + */ + public int create(String name) { + return create(name, false); + } + + /** + * Remove a parallel space. Returns 0 on success. + */ + public int remove(int userId) { + try { + return mParallelSpaceManager.remove(userId); + } catch (RemoteException e) { + throw new RuntimeException("Failed when remove(): " + e); + } + } + + /** + * Get a UserInfo list of current existing parallel users. + */ + public List getParallelUsers() { + try { + return new ArrayList<>(Arrays.asList(mParallelSpaceManager.getUsers())); + } catch (RemoteException e) { + throw new RuntimeException("Failed when getUsers(): " + e); + } + } + + /** + * Get a user id list of current existing parallel users. + */ + public List getParallelUserIds() { + ArrayList ret = new ArrayList<>(); + getParallelUsers().forEach(user -> ret.add(user.id)); + return ret; + } + + /** + * Get a UserHandle list of current existing parallel users. + */ + public List getParallelUserHandles() { + ArrayList ret = new ArrayList<>(); + getParallelUserIds().forEach(id -> ret.add(UserHandle.of(id))); + return ret; + } + + /** + * Get UserInfo of currently foreground parallel space owner. + */ + public UserInfo getParallelOwner() { + try { + return mParallelSpaceManager.getOwner(); + } catch (RemoteException e) { + throw new RuntimeException("Failed when getParallelOwner(): " + e); + } + } + + /** + * Get user id of currently foreground parallel space owner. + */ + public int getParallelOwnerId() { + UserInfo owner = getParallelOwner(); + return owner != null ? owner.id : 0; + } + + /** + * Get UserHandle of currently foreground parallel space owner. + */ + public UserHandle getParallelOwnerHandle() { + return UserHandle.of(getParallelOwnerId()); + } + + /** + * Install an existing package to target user. Returns 0 on success. + */ + public int duplicatePackage(String packageName, int userId) { + try { + return mParallelSpaceManager.duplicatePackage(packageName, userId); + } catch (RemoteException e) { + throw new RuntimeException("Failed when duplicatePackage(): " + e); + } + } + + /** + * Remove a package from target user. Returns 0 on success. + */ + public int removePackage(String packageName, int userId) { + try { + return mParallelSpaceManager.removePackage(packageName, userId); + } catch (RemoteException e) { + throw new RuntimeException("Failed when removePackage(): " + e); + } + } +} diff --git a/core/jni/Android.bp b/core/jni/Android.bp index aa661713b1fe..47d989be004a 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -50,6 +50,7 @@ cc_library_shared { "android_util_XmlBlock.cpp", "android_util_jar_StrictJarFile.cpp", "com_android_internal_util_VirtualRefBasePtr.cpp", + "com_android_internal_app_ActivityTrigger.cpp", ":deviceproductinfoconstants_aidl", ], diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index eba6cca76389..351ac91d3fd1 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -211,6 +211,7 @@ extern int register_com_android_internal_os_ZygoteInit(JNIEnv *env); extern int register_com_android_internal_security_VerityUtils(JNIEnv* env); extern int register_com_android_internal_util_VirtualRefBasePtr(JNIEnv *env); extern int register_android_window_WindowInfosListener(JNIEnv* env); +extern int register_com_android_internal_app_ActivityTrigger(JNIEnv *env); // Namespace for Android Runtime flags applied during boot time. static const char* RUNTIME_NATIVE_BOOT_NAMESPACE = "runtime_native_boot"; @@ -1651,6 +1652,7 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_animation_PropertyValuesHolder), REG_JNI(register_android_security_Scrypt), + REG_JNI(register_com_android_internal_app_ActivityTrigger), REG_JNI(register_com_android_internal_content_F2fsUtils), REG_JNI(register_com_android_internal_content_NativeLibraryHelper), REG_JNI(register_com_android_internal_os_FuseAppLoop), diff --git a/core/jni/android_hardware_Camera.cpp b/core/jni/android_hardware_Camera.cpp index 365a18d174c9..7000d9803060 100644 --- a/core/jni/android_hardware_Camera.cpp +++ b/core/jni/android_hardware_Camera.cpp @@ -470,6 +470,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) { @@ -783,7 +833,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)); @@ -1051,7 +1119,7 @@ static int32_t android_hardware_Camera_getAudioRestriction( //------------------------------------------------- static const JNINativeMethod camMethods[] = { - { "getNumberOfCameras", + { "_getNumberOfCameras", "()I", (void *)android_hardware_Camera_getNumberOfCameras }, { "_getCameraInfo", @@ -1096,6 +1164,18 @@ static const JNINativeMethod camMethods[] = { { "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 }, diff --git a/core/jni/android_media_AudioFormat.h b/core/jni/android_media_AudioFormat.h index 0e6b587db945..6a8c5f743840 100644 --- a/core/jni/android_media_AudioFormat.h +++ b/core/jni/android_media_AudioFormat.h @@ -48,6 +48,13 @@ #define ENCODING_DTS_UHD 27 #define ENCODING_DRA 28 +#define ENCODING_AMR_NB 100 +#define ENCODING_AMR_WB 101 +#define ENCODING_EVRC 102 +#define ENCODING_EVRC_B 103 +#define ENCODING_EVRC_WB 104 +#define ENCODING_EVRC_NW 105 + #define ENCODING_INVALID 0 #define ENCODING_DEFAULT 1 @@ -94,6 +101,18 @@ static inline audio_format_t audioFormatToNative(int audioFormat) return AUDIO_FORMAT_AC4; case ENCODING_E_AC3_JOC: return AUDIO_FORMAT_E_AC3_JOC; + case ENCODING_AMR_NB: + return AUDIO_FORMAT_AMR_NB; + case ENCODING_AMR_WB: + return AUDIO_FORMAT_AMR_WB; + case ENCODING_EVRC: + return AUDIO_FORMAT_EVRC; + case ENCODING_EVRC_B: + return AUDIO_FORMAT_EVRCB; + case ENCODING_EVRC_WB: + return AUDIO_FORMAT_EVRCWB; + case ENCODING_EVRC_NW: + return AUDIO_FORMAT_EVRCNW; case ENCODING_DEFAULT: return AUDIO_FORMAT_DEFAULT; case ENCODING_DOLBY_MAT: @@ -189,6 +208,18 @@ static inline int audioFormatFromNative(audio_format_t nativeFormat) return ENCODING_DTS_UHD; case AUDIO_FORMAT_DRA: return ENCODING_DRA; + case AUDIO_FORMAT_AMR_NB: + return ENCODING_AMR_NB; + case AUDIO_FORMAT_AMR_WB: + return ENCODING_AMR_WB; + case AUDIO_FORMAT_EVRC: + return ENCODING_EVRC; + case AUDIO_FORMAT_EVRCB: + return ENCODING_EVRC_B; + case AUDIO_FORMAT_EVRCWB: + return ENCODING_EVRC_WB; + case AUDIO_FORMAT_EVRCNW: + return ENCODING_EVRC_NW; case AUDIO_FORMAT_DEFAULT: return ENCODING_DEFAULT; default: diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp index 88aa4de0db24..e9d78e12b360 100644 --- a/core/jni/android_media_AudioSystem.cpp +++ b/core/jni/android_media_AudioSystem.cpp @@ -37,6 +37,7 @@ #include #include +#include #include "android_media_AudioAttributes.h" #include "android_media_AudioDescriptor.h" #include "android_media_AudioDeviceAttributes.h" @@ -193,6 +194,9 @@ static struct { static jclass gAudioDescriptorClass; static jmethodID gAudioDescriptorCstor; +static jclass gAppVolumeClass; +static jmethodID gAppVolumeCstor; + // // JNI Initialization for OpenSLES routing // @@ -843,6 +847,89 @@ android_media_AudioSystem_getMasterBalance(JNIEnv *env, jobject thiz) return balance; } + +static jint +android_media_AudioSystem_setAppVolume(JNIEnv *env, jobject thiz, jstring packageName, jfloat value) +{ + const jchar* c_packageName = env->GetStringCritical(packageName, 0); + String8 package8 = String8(reinterpret_cast(c_packageName), env->GetStringLength(packageName)); + env->ReleaseStringCritical(packageName, c_packageName); + return (jint) check_AudioSystem_Command(AudioSystem::setAppVolume(package8, value)); +} + +static jint +android_media_AudioSystem_setAppMute(JNIEnv *env, jobject thiz, jstring packageName, jboolean mute) +{ + const jchar* c_packageName = env->GetStringCritical(packageName, 0); + String8 package8 = String8(reinterpret_cast(c_packageName), env->GetStringLength(packageName)); + env->ReleaseStringCritical(packageName, c_packageName); + return (jint) check_AudioSystem_Command(AudioSystem::setAppMute(package8, mute)); +} + +jint convertAppVolumeFromNative(JNIEnv *env, jobject *jAppVolume, const media::AppVolume *AppVolume) +{ + jint jStatus = (jint)AUDIO_JAVA_SUCCESS; + jstring jPackageName; + jfloat jVolume; + jboolean jMute; + jboolean jActive; + + if (AppVolume == NULL || jAppVolume == NULL) { + jStatus = (jint)AUDIO_JAVA_ERROR; + goto exit; + } + + jPackageName = env->NewStringUTF(AppVolume->packageName); + jVolume = AppVolume->volume; + jMute = AppVolume->muted; + jActive = AppVolume->active; + + *jAppVolume = env->NewObject(gAppVolumeClass, gAppVolumeCstor, + jPackageName, jMute, jVolume, jActive); + + env->DeleteLocalRef(jPackageName); +exit: + return jStatus; +} + +static jint +android_media_AudioSystem_listAppVolumes(JNIEnv *env, jobject clazz, jobject jVolumes) +{ + ALOGV("listAppVolumes"); + + if (jVolumes == NULL) { + ALOGE("listAppVolumes NULL AppVolume ArrayList"); + return (jint)AUDIO_JAVA_BAD_VALUE; + } + if (!env->IsInstanceOf(jVolumes, gArrayListClass)) { + ALOGE("listAppVolumes not an arraylist"); + return (jint)AUDIO_JAVA_BAD_VALUE; + } + + std::vector volumes; + + jint jStatus = (jint)AUDIO_JAVA_SUCCESS; + status_t status = AudioSystem::listAppVolumes(&volumes); + + if (status != NO_ERROR) { + ALOGE("AudioSystem::listAppVolumes error %d", status); + jStatus = nativeToJavaStatus(status); + return jStatus; + } + + for (size_t i = 0; i < volumes.size(); i++) { + jobject jAppVolume; + jStatus = convertAppVolumeFromNative(env, &jAppVolume, &volumes[i]); + if (jStatus != AUDIO_JAVA_SUCCESS) { + return jStatus; + } + env->CallBooleanMethod(jVolumes, gArrayListMethods.add, jAppVolume); + env->DeleteLocalRef(jAppVolume); + } + + return jStatus; +} + static jint android_media_AudioSystem_getPrimaryOutputSamplingRate(JNIEnv *env, jobject clazz) { @@ -3104,7 +3191,13 @@ static const JNINativeMethod gMethods[] = (void *)android_media_AudioSystem_getDirectPlaybackSupport}, {"getDirectProfilesForAttributes", "(Landroid/media/AudioAttributes;Ljava/util/ArrayList;)I", - (void *)android_media_AudioSystem_getDirectProfilesForAttributes}}; + (void *)android_media_AudioSystem_getDirectProfilesForAttributes}, + {"setAppVolume", "(Ljava/lang/String;F)I", + (void *)android_media_AudioSystem_setAppVolume}, + {"setAppMute", "(Ljava/lang/String;Z)I", + (void *)android_media_AudioSystem_setAppMute}, + {"listAppVolumes", "(Ljava/util/ArrayList;)I", + (void *)android_media_AudioSystem_listAppVolumes}}; static const JNINativeMethod gEventHandlerMethods[] = { {"native_setup", @@ -3343,6 +3436,11 @@ int register_android_media_AudioSystem(JNIEnv *env) gVibratorMethods.getMaxAmplitude = GetMethodIDOrDie(env, vibratorClass, "getHapticChannelMaximumAmplitude", "()F"); + jclass AppVolumeClass = FindClassOrDie(env, "android/media/AppVolume"); + gAppVolumeClass = MakeGlobalRefOrDie(env, AppVolumeClass); + gAppVolumeCstor = GetMethodIDOrDie(env, AppVolumeClass, "", + "(Ljava/lang/String;ZFZ)V"); + AudioSystem::addErrorCallback(android_media_AudioSystem_error_callback); RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods)); diff --git a/core/jni/android_util_EventLog.cpp b/core/jni/android_util_EventLog.cpp index 0a5e78617568..2e9aa3405dac 100644 --- a/core/jni/android_util_EventLog.cpp +++ b/core/jni/android_util_EventLog.cpp @@ -68,16 +68,16 @@ static void android_util_EventLog_readEventsOnWrapping(JNIEnv* env, jobject claz */ static const JNINativeMethod gRegisterMethods[] = { /* name, signature, funcPtr */ - { "writeEvent", "(II)I", (void*) ELog::writeEventInteger }, - { "writeEvent", "(IJ)I", (void*) ELog::writeEventLong }, - { "writeEvent", "(IF)I", (void*) ELog::writeEventFloat }, - { "writeEvent", "(ILjava/lang/String;)I", (void*) ELog::writeEventString }, - { "writeEvent", "(I[Ljava/lang/Object;)I", (void*) ELog::writeEventArray }, - { "readEvents", + { "nativeWriteEvent", "(II)I", (void*) ELog::writeEventInteger }, + { "nativeWriteEvent", "(IJ)I", (void*) ELog::writeEventLong }, + { "nativeWriteEvent", "(IF)I", (void*) ELog::writeEventFloat }, + { "nativeWriteEvent", "(ILjava/lang/String;)I", (void*) ELog::writeEventString }, + { "nativeWriteEvent", "(I[Ljava/lang/Object;)I", (void*) ELog::writeEventArray }, + { "nativeReadEvents", "([ILjava/util/Collection;)V", (void*) android_util_EventLog_readEvents }, - { "readEventsOnWrapping", + { "nativeReadEventsOnWrapping", "([IJLjava/util/Collection;)V", (void*) android_util_EventLog_readEventsOnWrapping }, diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp index b9d5ee4b3015..4f19cdade122 100644 --- a/core/jni/android_util_Process.cpp +++ b/core/jni/android_util_Process.cpp @@ -279,6 +279,81 @@ void android_os_Process_setProcessGroup(JNIEnv* env, jobject clazz, int pid, jin signalExceptionForGroupError(env, errno ? errno : EPERM, pid); } +void android_os_Process_setCgroupProcsProcessGroup(JNIEnv* env, jobject clazz, int uid, int pid, jint grp, jboolean dex2oat_only) +{ + int fd; + char pathV1[255], pathV2[255]; + static bool isCgroupV2 = false; + if ((grp == SP_FOREGROUND) || (grp > SP_MAX)) { + signalExceptionForGroupError(env, EINVAL, pid); + return; + } + + //set process group for current process + android_os_Process_setProcessGroup(env, clazz, pid, grp); + + //find processes in the same cgroup.procs of current uid and pid + snprintf(pathV1, sizeof(pathV1), "/acct/uid_%d/pid_%d/cgroup.procs", uid, pid); + snprintf(pathV2, sizeof(pathV2), "/sys/fs/cgroup/uid_%d/pid_%d/cgroup.procs", uid, pid); + if (isCgroupV2) { + // read from V2 only + fd = open(pathV2, O_RDONLY); + } else { + // first try V1 + fd = open(pathV1, O_RDONLY); + if (fd < 0) { + fd = open(pathV2, O_RDONLY); + if (fd >= 0) { + isCgroupV2 = true; + } + } + } + if (fd >= 0) { + char buffer[256]; + char ch; + int numRead; + size_t len=0; + for (;;) { + numRead=read(fd, &ch, 1); + if (numRead <= 0) + break; + if (ch != '\n') { + buffer[len++] = ch; + } else { + int temp_pid = atoi(buffer); + len=0; + if (temp_pid == pid) + continue; + if (dex2oat_only) { + // check if cmdline of temp_pid is dex2oat + char cmdline[64]; + snprintf(cmdline, sizeof(cmdline), "/proc/%d/cmdline", temp_pid); + int cmdline_fd = open(cmdline, O_RDONLY); + if (cmdline_fd >= 0) { + size_t read_size = read(cmdline_fd, buffer, 255); + close(cmdline_fd); + buffer[read_size]='\0'; + const char *dex2oat_name1 = "dex2oat"; //for plugins compiler + const char *dex2oat_name2 = "/system/bin/dex2oat"; //for installer + const char *dex2oat_name3 = "/apex/com.android.runtime/bin/dex2oat"; //for installer + if (strncmp(buffer, dex2oat_name1, strlen(dex2oat_name1)) != 0 + && strncmp(buffer, dex2oat_name2, strlen(dex2oat_name2)) != 0 + && strncmp(buffer, dex2oat_name3, strlen(dex2oat_name3)) != 0) { + continue; + } + } else { + //ALOGE("read %s failed", cmdline); + continue; + } + } + //set cgroup of temp_pid follow pid + android_os_Process_setProcessGroup(env, clazz, temp_pid, grp); + } + } + close(fd); + } +} + void android_os_Process_setProcessFrozen( JNIEnv *env, jobject clazz, jint pid, jint uid, jboolean freeze) { @@ -356,8 +431,21 @@ static void get_cpuset_cores_for_policy(SchedPolicy policy, cpu_set_t *cpu_set) } break; case SP_FOREGROUND: + if (!CgroupGetAttributePath("HighCapacityCPUs", &filename)) { + return; + } + break; case SP_AUDIO_APP: case SP_AUDIO_SYS: + if (!CgroupGetAttributePath("AudioAppCapacityCPUs", &filename)) { + return; + } + if (access(filename.c_str(), F_OK) != 0) { + if (!CgroupGetAttributePath("HighCapacityCPUs", &filename)) { + return; + } + } + break; case SP_RT_APP: if (!CgroupGetAttributePath("HighCapacityCPUs", &filename)) { return; @@ -1263,6 +1351,7 @@ static const JNINativeMethod methods[] = { {"getThreadScheduler", "(I)I", (void*)android_os_Process_getThreadScheduler}, {"setThreadGroup", "(II)V", (void*)android_os_Process_setThreadGroup}, {"setThreadGroupAndCpuset", "(II)V", (void*)android_os_Process_setThreadGroupAndCpuset}, + {"setCgroupProcsProcessGroup", "(IIIZ)V", (void*)android_os_Process_setCgroupProcsProcessGroup}, {"setProcessGroup", "(II)V", (void*)android_os_Process_setProcessGroup}, {"getProcessGroup", "(I)I", (void*)android_os_Process_getProcessGroup}, {"createProcessGroup", "(II)I", (void*)android_os_Process_createProcessGroup}, diff --git a/core/jni/com_android_internal_app_ActivityTrigger.cpp b/core/jni/com_android_internal_app_ActivityTrigger.cpp new file mode 100644 index 000000000000..9d22f69d91ea --- /dev/null +++ b/core/jni/com_android_internal_app_ActivityTrigger.cpp @@ -0,0 +1,256 @@ +/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#define LOG_TAG "ActTriggerJNI" + +#include "jni.h" +#include +#include + +#include +#include +#include + +#include +#include + +namespace android +{ + +// ---------------------------------------------------------------------------- +/* + * Stuct containing handle to dynamically loaded lib as well as function + * pointers to key interfaces. + */ +typedef struct dlLibHandler { + void *dlhandle; + void (*startActivity)(const char *, int *); + void (*startApp)(const char *, int *); + void (*resumeActivity)(const char *); + void (*pauseActivity)(const char *); + void (*stopActivity)(const char *); + void (*init)(void); + void (*deinit)(void); + void (*miscActivity)(int, const char *, int, int, float *); + const char *dlname; +}dlLibHandler; + +/* + * Init for activity trigger library + */ +static dlLibHandler mDlLibHandler = { + NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, "libqti-at.so" +}; + +// ---------------------------------------------------------------------------- + +static void +com_android_internal_app_ActivityTrigger_native_at_init() +{ + bool errored = false; + + mDlLibHandler.dlhandle = dlopen(mDlLibHandler.dlname, RTLD_NOW | RTLD_LOCAL); + if (mDlLibHandler.dlhandle == NULL) { + return; + } + + *(void **) (&mDlLibHandler.startActivity) = dlsym(mDlLibHandler.dlhandle, "activity_trigger_start"); + if (mDlLibHandler.startActivity == NULL) { + errored = true; + } + + *(void **) (&mDlLibHandler.startApp) = dlsym(mDlLibHandler.dlhandle, "activity_trigger_qspm_start"); + + if (!errored) { + *(void **) (&mDlLibHandler.resumeActivity) = dlsym(mDlLibHandler.dlhandle, "activity_trigger_resume"); + if (mDlLibHandler.resumeActivity == NULL) { + errored = true; + } + } + if (!errored) { + *(void **) (&mDlLibHandler.pauseActivity) = dlsym(mDlLibHandler.dlhandle, "activity_trigger_pause"); + if (mDlLibHandler.pauseActivity == NULL) { + errored = true; + } + } + if (!errored) { + *(void **) (&mDlLibHandler.stopActivity) = dlsym(mDlLibHandler.dlhandle, "activity_trigger_stop"); + if (mDlLibHandler.stopActivity == NULL) { + errored = true; + } + } + if (!errored) { + *(void **) (&mDlLibHandler.init) = dlsym(mDlLibHandler.dlhandle, "activity_trigger_init"); + if (mDlLibHandler.init == NULL) { + errored = true; + } + } + if (!errored) { + *(void **) (&mDlLibHandler.miscActivity) = dlsym(mDlLibHandler.dlhandle, "activity_trigger_misc"); + if (mDlLibHandler.miscActivity == NULL) { + errored = true; + } + } + if (errored) { + mDlLibHandler.startActivity = NULL; + mDlLibHandler.startApp = NULL; + mDlLibHandler.resumeActivity = NULL; + mDlLibHandler.pauseActivity = NULL; + mDlLibHandler.stopActivity = NULL; + mDlLibHandler.miscActivity = NULL; + if (mDlLibHandler.dlhandle) { + dlclose(mDlLibHandler.dlhandle); + mDlLibHandler.dlhandle = NULL; + } + } else { + (*mDlLibHandler.init)(); + } +} + +static void +com_android_internal_app_ActivityTrigger_native_at_deinit(JNIEnv *env, jobject clazz) +{ + if (mDlLibHandler.dlhandle) { + mDlLibHandler.startActivity = NULL; + mDlLibHandler.startApp = NULL; + mDlLibHandler.resumeActivity = NULL; + mDlLibHandler.pauseActivity = NULL; + mDlLibHandler.stopActivity = NULL; + mDlLibHandler.miscActivity = NULL; + + *(void **) (&mDlLibHandler.deinit) = dlsym(mDlLibHandler.dlhandle, "activity_trigger_deinit"); + if (mDlLibHandler.deinit) { + (*mDlLibHandler.deinit)(); + } + + dlclose(mDlLibHandler.dlhandle); + mDlLibHandler.dlhandle = NULL; + } +} + +static jint +com_android_internal_app_ActivityTrigger_native_at_startActivity(JNIEnv *env, jobject clazz, jstring activity, jint flags) +{ + int activiyFlags = flags; + if(mDlLibHandler.startActivity && activity) { + const char *actStr = env->GetStringUTFChars(activity, NULL); + if (actStr) { + (*mDlLibHandler.startActivity)(actStr, &activiyFlags); + env->ReleaseStringUTFChars(activity, actStr); + } + } + return activiyFlags; +} + +static jint +com_android_internal_app_ActivityTrigger_native_at_startApp(JNIEnv *env, jobject clazz, jstring activity, jint flags) +{ + int activiyFlags = flags; + if(mDlLibHandler.startApp && activity) { + const char *actStr = env->GetStringUTFChars(activity, NULL); + if (actStr) { + (*mDlLibHandler.startApp)(actStr, &activiyFlags); + env->ReleaseStringUTFChars(activity, actStr); + } + } + return activiyFlags; +} + +static void +com_android_internal_app_ActivityTrigger_native_at_resumeActivity(JNIEnv *env, jobject clazz, jstring activity) +{ + if(mDlLibHandler.resumeActivity && activity) { + const char *actStr = env->GetStringUTFChars(activity, NULL); + if (actStr) { + (*mDlLibHandler.resumeActivity)(actStr); + env->ReleaseStringUTFChars(activity, actStr); + } + } +} + +static void +com_android_internal_app_ActivityTrigger_native_at_pauseActivity(JNIEnv *env, jobject clazz, jstring activity) +{ + if(mDlLibHandler.pauseActivity && activity) { + const char *actStr = env->GetStringUTFChars(activity, NULL); + if (NULL != actStr) { + (*mDlLibHandler.pauseActivity)(actStr); + env->ReleaseStringUTFChars(activity, actStr); + } + } +} + +static void +com_android_internal_app_ActivityTrigger_native_at_stopActivity(JNIEnv *env, jobject clazz, jstring activity) +{ + if(mDlLibHandler.stopActivity && activity) { + const char *actStr = env->GetStringUTFChars(activity, NULL); + if (NULL != actStr) { + (*mDlLibHandler.stopActivity)(actStr); + env->ReleaseStringUTFChars(activity, actStr); + } + } +} + +static jfloat +com_android_internal_app_ActivityTrigger_native_at_miscActivity(JNIEnv *env, jobject clazz, jint func, jstring activity, jint type, jint flag) +{ + float scaleValue = -1.0f; + if (mDlLibHandler.miscActivity && activity && func) { + const char *actStr = env->GetStringUTFChars(activity, NULL); + if (actStr) { + (*mDlLibHandler.miscActivity)(func, actStr, type, flag, &scaleValue); + env->ReleaseStringUTFChars(activity, actStr); + } + } + return scaleValue; +} + +// ---------------------------------------------------------------------------- + +static JNINativeMethod gMethods[] = { + {"native_at_startActivity", "(Ljava/lang/String;I)I", (void *)com_android_internal_app_ActivityTrigger_native_at_startActivity}, + {"native_at_startApp", "(Ljava/lang/String;I)I", (void *)com_android_internal_app_ActivityTrigger_native_at_startApp}, + {"native_at_resumeActivity", "(Ljava/lang/String;)V", (void *)com_android_internal_app_ActivityTrigger_native_at_resumeActivity}, + {"native_at_pauseActivity", "(Ljava/lang/String;)V", (void *)com_android_internal_app_ActivityTrigger_native_at_pauseActivity}, + {"native_at_stopActivity", "(Ljava/lang/String;)V", (void *)com_android_internal_app_ActivityTrigger_native_at_stopActivity}, + {"native_at_deinit", "()V", (void *)com_android_internal_app_ActivityTrigger_native_at_deinit}, + {"native_at_miscActivity", "(ILjava/lang/String;II)F", (void *)com_android_internal_app_ActivityTrigger_native_at_miscActivity}, +}; + +int register_com_android_internal_app_ActivityTrigger(JNIEnv *env) +{ + com_android_internal_app_ActivityTrigger_native_at_init(); + + return AndroidRuntime::registerNativeMethods(env, + "com/android/internal/app/ActivityTrigger", gMethods, NELEM(gMethods)); +} + +} // namespace android diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp index 21bbac0b0a7d..c56a5fe42c91 100644 --- a/core/jni/com_android_internal_os_Zygote.cpp +++ b/core/jni/com_android_internal_os_Zygote.cpp @@ -128,8 +128,6 @@ static jclass gZygoteInitClass; static jmethodID gGetOrCreateSystemServerClassLoader; static jmethodID gPrefetchStandaloneSystemServerJars; -static bool gIsSecurityEnforced = true; - /** * True if the app process is running in its mount namespace. */ @@ -634,11 +632,6 @@ static void PreApplicationInit() { } static void SetUpSeccompFilter(uid_t uid, bool is_child_zygote) { - if (!gIsSecurityEnforced) { - ALOGI("seccomp disabled by setenforce 0"); - return; - } - // Apply system or app filter based on uid. if (uid >= AID_APP_START) { if (is_child_zygote) { @@ -2317,11 +2310,6 @@ static void com_android_internal_os_Zygote_nativeAllowFileAcrossFork( static void com_android_internal_os_Zygote_nativeInstallSeccompUidGidFilter( JNIEnv* env, jclass, jint uidGidMin, jint uidGidMax) { - if (!gIsSecurityEnforced) { - ALOGI("seccomp disabled by setenforce 0"); - return; - } - bool installed = install_setuidgid_seccomp_filter(uidGidMin, uidGidMax); if (!installed) { RuntimeAbort(env, __LINE__, "Could not install setuid/setgid seccomp filter."); @@ -2398,10 +2386,6 @@ static void com_android_internal_os_Zygote_nativeInitNativeState(JNIEnv* env, jc * Security Initialization */ - // security_getenforce is not allowed on app process. Initialize and cache - // the value before zygote forks. - gIsSecurityEnforced = security_getenforce(); - selinux_android_seapp_context_init(); /* diff --git a/core/proto/android/os/processstarttime.proto b/core/proto/android/os/processstarttime.proto deleted file mode 100644 index d0f8baee7da2..000000000000 --- a/core/proto/android/os/processstarttime.proto +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -syntax = "proto2"; -package android.os; - -option java_multiple_files = true; - -// This message is used for statsd logging and should be kept in sync with -// frameworks/proto_logging/stats/atoms.proto -/** - * Logs information about process start time. - * - * Logged from: - * frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java - */ -message ProcessStartTime { - // The uid of the ProcessRecord. - optional int32 uid = 1; - - // The process pid. - optional int32 pid = 2; - - // The process name. - // Usually package name, "system" for system server. - // Provided by ActivityManagerService. - optional string process_name = 3; - - enum StartType { - UNKNOWN = 0; - WARM = 1; - HOT = 2; - COLD = 3; - } - - // The start type. - optional StartType type = 4; - - // The elapsed realtime at the start of the process. - optional int64 process_start_time_millis = 5; - - // Number of milliseconds it takes to reach bind application. - optional int32 bind_application_delay_millis = 6; - - // Number of milliseconds it takes to finish start of the process. - optional int32 process_start_delay_millis = 7; - - // hostingType field in ProcessRecord, the component type such as "activity", - // "service", "content provider", "broadcast" or other strings. - optional string hosting_type = 8; - - // hostingNameStr field in ProcessRecord. The component class name that runs - // in this process. - optional string hosting_name = 9; - - // Broadcast action name. - optional string broadcast_action_name = 10; - - enum HostingTypeId { - HOSTING_TYPE_UNKNOWN = 0; - HOSTING_TYPE_ACTIVITY = 1; - HOSTING_TYPE_ADDED_APPLICATION = 2; - HOSTING_TYPE_BACKUP = 3; - HOSTING_TYPE_BROADCAST = 4; - HOSTING_TYPE_CONTENT_PROVIDER = 5; - HOSTING_TYPE_LINK_FAIL = 6; - HOSTING_TYPE_ON_HOLD = 7; - HOSTING_TYPE_NEXT_ACTIVITY = 8; - HOSTING_TYPE_NEXT_TOP_ACTIVITY = 9; - HOSTING_TYPE_RESTART = 10; - HOSTING_TYPE_SERVICE = 11; - HOSTING_TYPE_SYSTEM = 12; - HOSTING_TYPE_TOP_ACTIVITY = 13; - HOSTING_TYPE_EMPTY = 14; - } - - optional HostingTypeId hosting_type_id = 11; -} - diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 5ae133bbe6e6..ccc011e2d9da 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -816,6 +816,10 @@ + + + + @@ -3006,6 +3010,10 @@ + + + + + + + + @@ -5292,6 +5315,11 @@ + + + @@ -6525,6 +6553,11 @@ + + + diff --git a/core/res/assets/images/android-logo-mask.png b/core/res/assets/images/android-logo-mask.png index 5512c0ad8a83..30d9e6f09e19 100644 Binary files a/core/res/assets/images/android-logo-mask.png and b/core/res/assets/images/android-logo-mask.png differ diff --git a/core/res/assets/images/android-logo-shine.png b/core/res/assets/images/android-logo-shine.png index c5d126392eeb..2ed7949fd910 100644 Binary files a/core/res/assets/images/android-logo-shine.png and b/core/res/assets/images/android-logo-shine.png differ diff --git a/core/res/remote_color_resources_res/values/colors.xml b/core/res/remote_color_resources_res/values/colors.xml index aff3a9592645..78a816b8ecba 100644 --- a/core/res/remote_color_resources_res/values/colors.xml +++ b/core/res/remote_color_resources_res/values/colors.xml @@ -66,4 +66,7 @@ #303030 #1b1b1b #000000 + + #83f6e5 + #00271e diff --git a/core/res/remote_color_resources_res/values/public.xml b/core/res/remote_color_resources_res/values/public.xml index 4b0a89202ad1..e1164df475b6 100644 --- a/core/res/remote_color_resources_res/values/public.xml +++ b/core/res/remote_color_resources_res/values/public.xml @@ -66,5 +66,8 @@ + + + diff --git a/core/res/res/color/config_progress_background_tint.xml b/core/res/res/color/config_progress_background_tint.xml index b086e20bf161..dfc914e76c8d 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/surface_header_dark_sysui.xml b/core/res/res/color/surface_header_dark_sysui.xml new file mode 100644 index 000000000000..ec070c96f91a --- /dev/null +++ b/core/res/res/color/surface_header_dark_sysui.xml @@ -0,0 +1,18 @@ + + + + + diff --git a/core/res/res/drawable-hdpi/numberpicker_selection_divider.9.png b/core/res/res/drawable-hdpi/numberpicker_selection_divider.9.png index c9c72ba61947..4e7eb8ad4324 100644 Binary files a/core/res/res/drawable-hdpi/numberpicker_selection_divider.9.png and b/core/res/res/drawable-hdpi/numberpicker_selection_divider.9.png differ diff --git a/core/res/res/drawable-hdpi/stat_notify_missed_call.png b/core/res/res/drawable-hdpi/stat_notify_missed_call.png index f205471bc5f1..37f478b1b1a2 100644 Binary files a/core/res/res/drawable-hdpi/stat_notify_missed_call.png and b/core/res/res/drawable-hdpi/stat_notify_missed_call.png differ diff --git a/core/res/res/drawable-hdpi/stat_sys_data_usb.png b/core/res/res/drawable-hdpi/stat_sys_data_usb.png deleted file mode 100644 index f14f9080c121..000000000000 Binary files a/core/res/res/drawable-hdpi/stat_sys_data_usb.png and /dev/null differ diff --git a/core/res/res/drawable-hdpi/tab_selected_holo.9.png b/core/res/res/drawable-hdpi/tab_selected_holo.9.png deleted file mode 100644 index d57df98b5019..000000000000 Binary files a/core/res/res/drawable-hdpi/tab_selected_holo.9.png and /dev/null differ diff --git a/core/res/res/drawable-ldpi/stat_sys_data_usb.png b/core/res/res/drawable-ldpi/stat_sys_data_usb.png deleted file mode 100644 index ffaccbde6952..000000000000 Binary files a/core/res/res/drawable-ldpi/stat_sys_data_usb.png and /dev/null differ diff --git a/core/res/res/drawable-mdpi/numberpicker_selection_divider.9.png b/core/res/res/drawable-mdpi/numberpicker_selection_divider.9.png index 076fc1664217..97d250f889a2 100644 Binary files a/core/res/res/drawable-mdpi/numberpicker_selection_divider.9.png and b/core/res/res/drawable-mdpi/numberpicker_selection_divider.9.png differ diff --git a/core/res/res/drawable-mdpi/stat_notify_missed_call.png b/core/res/res/drawable-mdpi/stat_notify_missed_call.png index f2ff56e21ba7..cd73b12e5569 100644 Binary files a/core/res/res/drawable-mdpi/stat_notify_missed_call.png and b/core/res/res/drawable-mdpi/stat_notify_missed_call.png differ diff --git a/core/res/res/drawable-mdpi/stat_sys_data_usb.png b/core/res/res/drawable-mdpi/stat_sys_data_usb.png deleted file mode 100644 index 40d77f0a38f5..000000000000 Binary files a/core/res/res/drawable-mdpi/stat_sys_data_usb.png and /dev/null differ diff --git a/core/res/res/drawable-mdpi/tab_selected_holo.9.png b/core/res/res/drawable-mdpi/tab_selected_holo.9.png deleted file mode 100644 index 587337caf74f..000000000000 Binary files a/core/res/res/drawable-mdpi/tab_selected_holo.9.png and /dev/null differ diff --git a/core/res/res/drawable-xhdpi/numberpicker_selection_divider.9.png b/core/res/res/drawable-xhdpi/numberpicker_selection_divider.9.png index 97eb5fe801ea..a893b5790a6a 100644 Binary files a/core/res/res/drawable-xhdpi/numberpicker_selection_divider.9.png and b/core/res/res/drawable-xhdpi/numberpicker_selection_divider.9.png differ diff --git a/core/res/res/drawable-xhdpi/stat_notify_missed_call.png b/core/res/res/drawable-xhdpi/stat_notify_missed_call.png index 8719eff5ae1a..b0f2b87538df 100644 Binary files a/core/res/res/drawable-xhdpi/stat_notify_missed_call.png and b/core/res/res/drawable-xhdpi/stat_notify_missed_call.png differ diff --git a/core/res/res/drawable-xhdpi/stat_sys_data_usb.png b/core/res/res/drawable-xhdpi/stat_sys_data_usb.png deleted file mode 100644 index 57c1099d0d55..000000000000 Binary files a/core/res/res/drawable-xhdpi/stat_sys_data_usb.png and /dev/null differ diff --git a/core/res/res/drawable-xhdpi/tab_selected_holo.9.png b/core/res/res/drawable-xhdpi/tab_selected_holo.9.png deleted file mode 100644 index e4229f26b277..000000000000 Binary files a/core/res/res/drawable-xhdpi/tab_selected_holo.9.png and /dev/null differ diff --git a/core/res/res/drawable-xxhdpi/numberpicker_selection_divider.9.png b/core/res/res/drawable-xxhdpi/numberpicker_selection_divider.9.png index b7a99402eb58..3b6fa1a6c407 100644 Binary files a/core/res/res/drawable-xxhdpi/numberpicker_selection_divider.9.png and b/core/res/res/drawable-xxhdpi/numberpicker_selection_divider.9.png differ diff --git a/core/res/res/drawable-xxhdpi/stat_notify_missed_call.png b/core/res/res/drawable-xxhdpi/stat_notify_missed_call.png index 904df3baa20d..f8c6f9b14c67 100644 Binary files a/core/res/res/drawable-xxhdpi/stat_notify_missed_call.png and b/core/res/res/drawable-xxhdpi/stat_notify_missed_call.png differ diff --git a/core/res/res/drawable-xxhdpi/stat_sys_data_usb.png b/core/res/res/drawable-xxhdpi/stat_sys_data_usb.png deleted file mode 100644 index 7fcf5cd999ab..000000000000 Binary files a/core/res/res/drawable-xxhdpi/stat_sys_data_usb.png and /dev/null differ diff --git a/core/res/res/drawable-xxhdpi/tab_selected_holo.9.png b/core/res/res/drawable-xxhdpi/tab_selected_holo.9.png deleted file mode 100644 index bee35cad65f6..000000000000 Binary files a/core/res/res/drawable-xxhdpi/tab_selected_holo.9.png and /dev/null differ diff --git a/core/res/res/drawable-xxxhdpi/stat_notify_missed_call.png b/core/res/res/drawable-xxxhdpi/stat_notify_missed_call.png new file mode 100644 index 000000000000..9e604de298df Binary files /dev/null and b/core/res/res/drawable-xxxhdpi/stat_notify_missed_call.png differ diff --git a/core/res/res/drawable/ic_account_circle.xml b/core/res/res/drawable/ic_account_circle.xml index 71691add7322..b83598c51fb7 100644 --- a/core/res/res/drawable/ic_account_circle.xml +++ b/core/res/res/drawable/ic_account_circle.xml @@ -20,8 +20,5 @@ Copyright (C) 2014 The Android Open Source Project android:viewportHeight="24"> - + android:pathData="M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M7.07,18.28C7.5,17.38 10.12,16.5 12,16.5C13.88,16.5 16.5,17.38 16.93,18.28C15.57,19.36 13.86,20 12,20C10.14,20 8.43,19.36 7.07,18.28M18.36,16.83C16.93,15.09 13.46,14.5 12,14.5C10.54,14.5 7.07,15.09 5.64,16.83C4.62,15.5 4,13.82 4,12C4,7.59 7.59,4 12,4C16.41,4 20,7.59 20,12C20,13.82 19.38,15.5 18.36,16.83M12,6C10.06,6 8.5,7.56 8.5,9.5C8.5,11.44 10.06,13 12,13C13.94,13 15.5,11.44 15.5,9.5C15.5,7.56 13.94,6 12,6M12,11A1.5,1.5 0 0,1 10.5,9.5A1.5,1.5 0 0,1 12,8A1.5,1.5 0 0,1 13.5,9.5A1.5,1.5 0 0,1 12,11Z" /> diff --git a/core/res/res/drawable/ic_audio_media.xml b/core/res/res/drawable/ic_audio_media.xml index 4ef5340138b1..fd3c0deb75b6 100644 --- a/core/res/res/drawable/ic_audio_media.xml +++ b/core/res/res/drawable/ic_audio_media.xml @@ -22,7 +22,7 @@ + android:pathData="M12,3l0.01,10.55c-0.59,-0.34 -1.27,-0.55 -2,-0.55C7.79,13 6,14.79 6,17c0,2.21 1.79,4 4.01,4S14,19.21 14,17V7h4V3H12zM10.01,19c-1.1,0 -2,-0.9 -2,-2c0,-1.1 0.9,-2 2,-2s2,0.9 2,2C12.01,18.1 11.11,19 10.01,19z"/> diff --git a/core/res/res/drawable/ic_audio_media_mute.xml b/core/res/res/drawable/ic_audio_media_mute.xml index 2be6dc42af4d..210aee81451c 100644 --- a/core/res/res/drawable/ic_audio_media_mute.xml +++ b/core/res/res/drawable/ic_audio_media_mute.xml @@ -22,10 +22,10 @@ + android:pathData="M21.19,21.19L14,14l-2,-2l-9.2,-9.2L1.39,4.22l8.79,8.79c-0.06,0 -0.12,-0.01 -0.18,-0.01C7.79,13 6,14.79 6,17c0,2.21 1.79,4 4.01,4S14,19.21 14,17v-0.17l5.78,5.78L21.19,21.19zM10.01,19c-1.1,0 -2,-0.9 -2,-2c0,-1.1 0.9,-2 2,-2s2,0.9 2,2C12.01,18.1 11.11,19 10.01,19z"/> + android:pathData="M14,11.17l0,-4.17l4,0l0,-4l-6,0l0,6.17z"/> diff --git a/core/res/res/drawable/ic_audio_ring_notif.xml b/core/res/res/drawable/ic_audio_ring_notif.xml index 54c4074771c2..47a08966aec2 100644 --- a/core/res/res/drawable/ic_audio_ring_notif.xml +++ b/core/res/res/drawable/ic_audio_ring_notif.xml @@ -22,5 +22,8 @@ Copyright (C) 2014 The Android Open Source Project + android:pathData="M18,17v-6c0,-3.07 -1.63,-5.64 -4.5,-6.32V4c0,-0.83 -0.67,-1.5 -1.5,-1.5S10.5,3.17 10.5,4v0.68C7.64,5.36 6,7.92 6,11v6H4v2h10h0.38H20v-2H18zM16,17H8v-6c0,-2.48 1.51,-4.5 4,-4.5s4,2.02 4,4.5V17z"/> + diff --git a/core/res/res/drawable/ic_audio_ring_notif_mute.xml b/core/res/res/drawable/ic_audio_ring_notif_mute.xml index b5915207b7f1..c838fe245d11 100644 --- a/core/res/res/drawable/ic_audio_ring_notif_mute.xml +++ b/core/res/res/drawable/ic_audio_ring_notif_mute.xml @@ -22,5 +22,11 @@ Copyright (C) 2014 The Android Open Source Project + android:pathData="M12,22c1.1,0 2,-0.9 2,-2h-4C10,21.1 10.9,22 12,22z"/> + + diff --git a/core/res/res/drawable/ic_audio_vol.xml b/core/res/res/drawable/ic_audio_vol.xml index fc216e5b699a..f66c316b897e 100644 --- a/core/res/res/drawable/ic_audio_vol.xml +++ b/core/res/res/drawable/ic_audio_vol.xml @@ -16,10 +16,10 @@ Copyright (C) 2014 The Android Open Source Project + android:pathData="M3 9v6h4l5 5V4L7 9H3zm7-0.17v6.34L7.83 13H5v-2h2.83L10 8.83zM16.5 12c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-0.73 2.5-2.25 2.5-4.02zM14 3.23v2.06c2.89 0.86 5 3.54 5 6.71s-2.11 5.85-5 6.71v2.06c4.01-0.91 7-4.49 7-8.77 0-4.28-2.99-7.86-7-8.77z" /> diff --git a/core/res/res/drawable/ic_audio_vol_mute.xml b/core/res/res/drawable/ic_audio_vol_mute.xml index 7cf604c614be..d102676fe126 100644 --- a/core/res/res/drawable/ic_audio_vol_mute.xml +++ b/core/res/res/drawable/ic_audio_vol_mute.xml @@ -16,10 +16,10 @@ Copyright (C) 2014 The Android Open Source Project + android:pathData="M4.34 2.93L2.93 4.34 7.29 8.7 7 9H3v6h4l5 5v-6.59l4.18 4.18c-0.65 0.49 -1.38 0.88 -2.18 1.11v2.06c1.34-0.3 2.57-0.92 3.61-1.75l2.05 2.05 1.41-1.41L4.34 2.93zM10 15.17L7.83 13H5v-2h2.83l0.88-0.88L10 11.41v3.76zM19 12c0 0.82-0.15 1.61-0.41 2.34l1.53 1.53c0.56-1.17 0.88 -2.48 0.88 -3.87 0-4.28-2.99-7.86-7-8.77v2.06c2.89 0.86 5 3.54 5 6.71zm-7-8l-1.88 1.88L12 7.76zm4.5 8c0-1.77-1.02-3.29-2.5-4.03v1.79l2.48 2.48c0.01-0.08 0.02 -0.16 0.02 -0.24z" /> diff --git a/core/res/res/drawable/ic_battery.xml b/core/res/res/drawable/ic_battery.xml index bd40f4df505e..318d125ed256 100644 --- a/core/res/res/drawable/ic_battery.xml +++ b/core/res/res/drawable/ic_battery.xml @@ -21,5 +21,5 @@ android:tint="?android:attr/colorControlNormal"> + android:pathData="M16,20H8V6H16M16.67,4H15V2H9V4H7.33A1.33,1.33 0 0,0 6,5.33V20.67C6,21.4 6.6,22 7.33,22H16.67A1.33,1.33 0 0,0 18,20.67V5.33C18,4.6 17.4,4 16.67,4Z" /> diff --git a/core/res/res/drawable/ic_bt_headphones_a2dp.xml b/core/res/res/drawable/ic_bt_headphones_a2dp.xml index 32f39a39754f..aa53b61387fb 100644 --- a/core/res/res/drawable/ic_bt_headphones_a2dp.xml +++ b/core/res/res/drawable/ic_bt_headphones_a2dp.xml @@ -21,6 +21,5 @@ android:tint="?android:attr/colorControlNormal"> + android:pathData="M19,15v3c0,0.55 -0.45,1 -1,1h-1v-4h2M7,15v4H6c-0.55,0 -1,-0.45 -1,-1v-3h2m5,-13c-4.97,0 -9,4.03 -9,9v7c0,1.66 1.34,3 3,3h3v-8H5v-2c0,-3.87 3.13,-7 7,-7s7,3.13 7,7v2h-4v8h3c1.66,0 3,-1.34 3,-3v-7c0,-4.97 -4.03,-9 -9,-9z" /> \ No newline at end of file diff --git a/core/res/res/drawable/ic_bt_headset_hfp.xml b/core/res/res/drawable/ic_bt_headset_hfp.xml index e43fe39409af..f2066ed7c6cd 100644 --- a/core/res/res/drawable/ic_bt_headset_hfp.xml +++ b/core/res/res/drawable/ic_bt_headset_hfp.xml @@ -21,7 +21,5 @@ android:tint="?android:attr/colorControlNormal"> + android:pathData="M12,1c-4.97,0 -9,4.03 -9,9v7c0,1.66 1.34,3 3,3h3v-8H5v-1.71C5,6.45 7.96,3.11 11.79,3C15.76,2.89 19,6.06 19,10v2h-4v8h4v1h-6v2h6c1.1,0 2,-0.9 2,-2V10C21,5.03 16.97,1 12,1zM7,14v4H6c-0.55,0 -1,-0.45 -1,-1v-3H7zM19,18h-2v-4h2V18z" /> \ No newline at end of file diff --git a/core/res/res/drawable/ic_bt_pointing_hid.xml b/core/res/res/drawable/ic_bt_pointing_hid.xml index de97e249789f..470d1528b4fb 100644 --- a/core/res/res/drawable/ic_bt_pointing_hid.xml +++ b/core/res/res/drawable/ic_bt_pointing_hid.xml @@ -21,6 +21,5 @@ android:tint="?android:attr/colorControlNormal"> + android:pathData="M20 9c-0.04-4.39-3.6-7.93-8-7.93S4.04 4.61 4 9v6c0 4.42 3.58 8 8 8s8-3.58 8-8V9zm-2 0h-5V3.16c2.81 0.47 4.96 2.9 5 5.84zm-7-5.84V9H6c0.04-2.94 2.19-5.37 5-5.84zM18 15c0 3.31-2.69 6-6 6s-6-2.69-6-6v-4h12v4z" /> \ No newline at end of file diff --git a/core/res/res/drawable/ic_doc_folder.xml b/core/res/res/drawable/ic_doc_folder.xml index dcbce010810e..ac29ba665609 100644 --- a/core/res/res/drawable/ic_doc_folder.xml +++ b/core/res/res/drawable/ic_doc_folder.xml @@ -20,5 +20,5 @@ Copyright (C) 2015 The Android Open Source Project android:viewportHeight="24.0"> + android:pathData="M20,18H4V8H20M20,6H12L10,4H4C2.89,4 2,4.89 2,6V18A2,2 0 0,0 4,20H20A2,2 0 0,0 22,18V8C22,6.89 21.1,6 20,6Z" /> diff --git a/core/res/res/drawable/ic_eject_24dp.xml b/core/res/res/drawable/ic_eject_24dp.xml index 321ee3b6289c..d01461aa0232 100644 --- a/core/res/res/drawable/ic_eject_24dp.xml +++ b/core/res/res/drawable/ic_eject_24dp.xml @@ -20,8 +20,5 @@ Copyright (C) 2015 The Android Open Source Project android:viewportHeight="24.0"> - + android:pathData="M5,17H19V19H5V17M12,5L5.33,15H18.67L12,5M12,8.6L14.93,13H9.07L12,8.6Z" /> diff --git a/core/res/res/drawable/ic_file_copy.xml b/core/res/res/drawable/ic_file_copy.xml index d05b55f1279f..01dff735a402 100644 --- a/core/res/res/drawable/ic_file_copy.xml +++ b/core/res/res/drawable/ic_file_copy.xml @@ -20,6 +20,6 @@ android:viewportWidth="24" android:viewportHeight="24"> diff --git a/core/res/res/drawable/ic_folder_24dp.xml b/core/res/res/drawable/ic_folder_24dp.xml index 9a386ca45e7a..b6d8a1bbf9c6 100644 --- a/core/res/res/drawable/ic_folder_24dp.xml +++ b/core/res/res/drawable/ic_folder_24dp.xml @@ -16,9 +16,9 @@ Copyright (C) 2015 The Android Open Source Project + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M20,18H4V8H20M20,6H12L10,4H4C2.89,4 2,4.89 2,6V18A2,2 0 0,0 4,20H20A2,2 0 0,0 22,18V8C22,6.89 21.1,6 20,6Z" /> diff --git a/core/res/res/drawable/ic_lock_lockdown.xml b/core/res/res/drawable/ic_lock_lockdown.xml index b9685d3e7cca..6c6a676a6d35 100644 --- a/core/res/res/drawable/ic_lock_lockdown.xml +++ b/core/res/res/drawable/ic_lock_lockdown.xml @@ -22,5 +22,5 @@ Copyright (C) 2018 The Android Open Source Project + android:pathData="M12,17C10.89,17 10,16.1 10,15C10,13.89 10.89,13 12,13A2,2 0 0,1 14,15A2,2 0 0,1 12,17M18,20V10H6V20H18M18,8A2,2 0 0,1 20,10V20A2,2 0 0,1 18,22H6C4.89,22 4,21.1 4,20V10C4,8.89 4.89,8 6,8H7V6A5,5 0 0,1 12,1A5,5 0 0,1 17,6V8H18M12,3A3,3 0 0,0 9,6V8H15V6A3,3 0 0,0 12,3Z" /> diff --git a/core/res/res/drawable/ic_lockscreen_ime.xml b/core/res/res/drawable/ic_lockscreen_ime.xml index 4b81a3c9c460..90492770c5bd 100644 --- a/core/res/res/drawable/ic_lockscreen_ime.xml +++ b/core/res/res/drawable/ic_lockscreen_ime.xml @@ -21,6 +21,5 @@ android:tint="?android:attr/colorControlNormal"> + android:pathData="M4,5A2,2 0 0,0 2,7V17A2,2 0 0,0 4,19H20A2,2 0 0,0 22,17V7A2,2 0 0,0 20,5H4M4,7H20V17H4V7M5,8V10H7V8H5M8,8V10H10V8H8M11,8V10H13V8H11M14,8V10H16V8H14M17,8V10H19V8H17M5,11V13H7V11H5M8,11V13H10V11H8M11,11V13H13V11H11M14,11V13H16V11H14M17,11V13H19V11H17M8,14V16H16V14H8Z" /> \ No newline at end of file diff --git a/core/res/res/drawable/ic_notification_alert.xml b/core/res/res/drawable/ic_notification_alert.xml index c8514acde2ac..f9bd88a9faa7 100644 --- a/core/res/res/drawable/ic_notification_alert.xml +++ b/core/res/res/drawable/ic_notification_alert.xml @@ -25,9 +25,6 @@ Copyright (C) 2016 The Android Open Source Project android:pathData="M7.1,3.6L5.7,2.2C3.3,4.0 1.7,6.8 1.5,10.0l2.0,0.0C3.7,7.3 5.0,5.0 7.1,3.6z" android:fillColor="#FFFFFFFF"/> - diff --git a/core/res/res/drawable/ic_notifications_alerted.xml b/core/res/res/drawable/ic_notifications_alerted.xml index 4bfac37e8408..6bbca37cd48c 100644 --- a/core/res/res/drawable/ic_notifications_alerted.xml +++ b/core/res/res/drawable/ic_notifications_alerted.xml @@ -19,6 +19,9 @@ Copyright (C) 2018 The Android Open Source Project android:viewportWidth="24.0" android:viewportHeight="24.0"> + diff --git a/core/res/res/drawable/ic_parallel_badge.xml b/core/res/res/drawable/ic_parallel_badge.xml new file mode 100644 index 000000000000..3c5734c7e0fc --- /dev/null +++ b/core/res/res/drawable/ic_parallel_badge.xml @@ -0,0 +1,31 @@ + + + + + + + + diff --git a/core/res/res/drawable/ic_parallel_icon_badge.xml b/core/res/res/drawable/ic_parallel_icon_badge.xml new file mode 100644 index 000000000000..c86ca733de04 --- /dev/null +++ b/core/res/res/drawable/ic_parallel_icon_badge.xml @@ -0,0 +1,40 @@ + + + + + + + + + + diff --git a/core/res/res/drawable/ic_perm_device_info.xml b/core/res/res/drawable/ic_perm_device_info.xml index ef91c74620ac..b546992bd39d 100644 --- a/core/res/res/drawable/ic_perm_device_info.xml +++ b/core/res/res/drawable/ic_perm_device_info.xml @@ -16,9 +16,9 @@ + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M17,1.01L7,1C5.9,1 5,1.9 5,3v18c0,1.1 0.9,2 2,2h10c1.1,0 2,-0.9 2,-2V3C19,1.9 18.1,1.01 17,1.01zM17,21H7v-1h10V21zM17,18H7V6h10V18zM7,4V3h10v1H7zM11,7h2v2h-2V7zM11,11h2v6h-2V11z"/> diff --git a/core/res/res/drawable/ic_phone.xml b/core/res/res/drawable/ic_phone.xml index be9b094134c8..27ada123149f 100644 --- a/core/res/res/drawable/ic_phone.xml +++ b/core/res/res/drawable/ic_phone.xml @@ -22,8 +22,5 @@ android:autoMirrored="true"> + android:pathData="M6.54 5c0.06 0.89 0.21 1.76 0.45 2.59l-1.2 1.2c-0.41-1.2-0.67-2.47-0.76-3.79h1.51m9.86 12.02c0.85 0.24 1.72 0.39 2.6 0.45 v1.49c-1.32-0.09-2.59-0.35-3.8-0.75l1.2-1.19M7.5 3H4c-0.55 0-1 0.45-1 1 0 9.39 7.61 17 17 17 0.55 0 1-0.45 1-1v-3.49c0-0.55-0.45-1-1-1-1.24 0-2.45-0.2-3.57-0.57-0.1-0.04-0.21-0.05-0.31-0.05-0.26 0-0.51 0.1 -0.71 0.29 l-2.2 2.2c-2.83-1.45-5.15-3.76-6.59-6.59l2.2-2.2c0.28-0.28 0.36 -0.67 0.25 -1.02C8.7 6.45 8.5 5.25 8.5 4c0-0.55-0.45-1-1-1z" /> \ No newline at end of file diff --git a/core/res/res/drawable/ic_pocket_lock.xml b/core/res/res/drawable/ic_pocket_lock.xml new file mode 100644 index 000000000000..3494a8f38b84 --- /dev/null +++ b/core/res/res/drawable/ic_pocket_lock.xml @@ -0,0 +1,10 @@ + + + diff --git a/core/res/res/drawable/ic_print.xml b/core/res/res/drawable/ic_print.xml index 7aa251300448..b72b4d02946e 100644 --- a/core/res/res/drawable/ic_print.xml +++ b/core/res/res/drawable/ic_print.xml @@ -21,6 +21,9 @@ android:viewportWidth="24.0" android:viewportHeight="24.0"> + \ No newline at end of file diff --git a/core/res/res/drawable/ic_print_error.xml b/core/res/res/drawable/ic_print_error.xml index 37e51527e399..999e92e39e07 100644 --- a/core/res/res/drawable/ic_print_error.xml +++ b/core/res/res/drawable/ic_print_error.xml @@ -21,6 +21,6 @@ android:viewportWidth="24.0" android:viewportHeight="24.0"> \ No newline at end of file diff --git a/core/res/res/drawable/ic_qs_night_display_on.xml b/core/res/res/drawable/ic_qs_night_display_on.xml index a4755ee256e2..68004f168f2e 100644 --- a/core/res/res/drawable/ic_qs_night_display_on.xml +++ b/core/res/res/drawable/ic_qs_night_display_on.xml @@ -21,6 +21,6 @@ + android:pathData="M17.75,4.09L15.22,6.03L16.13,9.09L13.5,7.28L10.87,9.09L11.78,6.03L9.25,4.09L12.44,4L13.5,1L14.56,4L17.75,4.09M21.25,11L19.61,12.25L20.2,14.23L18.5,13.06L16.8,14.23L17.39,12.25L15.75,11L17.81,10.95L18.5,9L19.19,10.95L21.25,11M18.97,15.95C19.8,15.87 20.69,17.05 20.16,17.8C19.84,18.25 19.5,18.67 19.08,19.07C15.17,23 8.84,23 4.94,19.07C1.03,15.17 1.03,8.83 4.94,4.93C5.34,4.53 5.76,4.17 6.21,3.85C6.96,3.32 8.14,4.21 8.06,5.04C7.79,7.9 8.75,10.87 10.95,13.06C13.14,15.26 16.1,16.22 18.97,15.95M17.33,17.97C14.5,17.81 11.7,16.64 9.53,14.5C7.36,12.31 6.2,9.5 6.04,6.68C3.23,9.82 3.34,14.64 6.35,17.66C9.37,20.67 14.19,20.78 17.33,17.97Z" /> diff --git a/core/res/res/drawable/ic_sd_card_48dp.xml b/core/res/res/drawable/ic_sd_card_48dp.xml index 10fd12054820..f3927e95cb22 100644 --- a/core/res/res/drawable/ic_sd_card_48dp.xml +++ b/core/res/res/drawable/ic_sd_card_48dp.xml @@ -16,9 +16,9 @@ Copyright (C) 2015 The Android Open Source Project + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M18 4v16H6V8.83L10.83 4H18m0-2h-8L4 8v12c0 1.1 0.9 2 2 2h12c1.1 0 2-0.9 2-2V4c0-1.1-0.9-2-2-2zM9 7h2v4H9zm3 0h2v4h-2zm3 0h2v4h-2z"/> diff --git a/core/res/res/drawable/ic_settings_print.xml b/core/res/res/drawable/ic_settings_print.xml index 68b627cf4dc9..b77e6ba0f2d8 100644 --- a/core/res/res/drawable/ic_settings_print.xml +++ b/core/res/res/drawable/ic_settings_print.xml @@ -21,5 +21,8 @@ android:tint="?android:attr/colorControlNormal"> + android:pathData="M19 8h-1V3H6v5H5c-1.66 0-3 1.34-3 3v6h4v4h12v-4h4v-6c0-1.66-1.34-3-3-3zM8 5h8v3H8V5zm8 12v2H8v-4h8v2zm2-2v-2H6v2H4v-4c0-0.55 0.45 -1 1-1h14c0.55 0 1 0.45 1 1v4h-2z" /> + diff --git a/core/res/res/drawable/ic_voice_search_api_material.xml b/core/res/res/drawable/ic_voice_search_api_material.xml index a02621820b62..5aa5b56faa2d 100644 --- a/core/res/res/drawable/ic_voice_search_api_material.xml +++ b/core/res/res/drawable/ic_voice_search_api_material.xml @@ -20,6 +20,9 @@ Copyright (C) 2014 The Android Open Source Project android:viewportHeight="24.0" android:tint="?attr/colorControlNormal"> + diff --git a/core/res/res/drawable/perm_group_call_log.xml b/core/res/res/drawable/perm_group_call_log.xml index a37ed88bebfc..e60770f7b160 100644 --- a/core/res/res/drawable/perm_group_call_log.xml +++ b/core/res/res/drawable/perm_group_call_log.xml @@ -23,7 +23,7 @@ android:viewportHeight="24.0"> + android:pathData="M6.54 5c0.06 0.89 0.21 1.76 0.45 2.59l-1.2 1.2c-0.41-1.2-0.67-2.47-0.76-3.79h1.51m9.86 12.02c0.85 0.24 1.72 0.39 2.6 0.45 v1.49c-1.32-0.09-2.59-0.35-3.8-0.75l1.2-1.19M7.5 3H4c-0.55 0-1 0.45-1 1 0 9.39 7.61 17 17 17 0.55 0 1-0.45 1-1v-3.49c0-0.55-0.45-1-1-1-1.24 0-2.45-0.2-3.57-0.57-0.1-0.04-0.21-0.05-0.31-0.05-0.26 0-0.51 0.1 -0.71 0.29 l-2.2 2.2c-2.83-1.45-5.15-3.76-6.59-6.59l2.2-2.2c0.28-0.28 0.36 -0.67 0.25 -1.02C8.7 6.45 8.5 5.25 8.5 4c0-0.55-0.45-1-1-1z" /> diff --git a/core/res/res/drawable/perm_group_phone_calls.xml b/core/res/res/drawable/perm_group_phone_calls.xml index 563222698b46..ff4c138013b1 100644 --- a/core/res/res/drawable/perm_group_phone_calls.xml +++ b/core/res/res/drawable/perm_group_phone_calls.xml @@ -22,8 +22,5 @@ android:viewportHeight="24"> + android:pathData="M6.54 5c0.06 0.89 0.21 1.76 0.45 2.59l-1.2 1.2c-0.41-1.2-0.67-2.47-0.76-3.79h1.51m9.86 12.02c0.85 0.24 1.72 0.39 2.6 0.45 v1.49c-1.32-0.09-2.59-0.35-3.8-0.75l1.2-1.19M7.5 3H4c-0.55 0-1 0.45-1 1 0 9.39 7.61 17 17 17 0.55 0 1-0.45 1-1v-3.49c0-0.55-0.45-1-1-1-1.24 0-2.45-0.2-3.57-0.57-0.1-0.04-0.21-0.05-0.31-0.05-0.26 0-0.51 0.1 -0.71 0.29 l-2.2 2.2c-2.83-1.45-5.15-3.76-6.59-6.59l2.2-2.2c0.28-0.28 0.36 -0.67 0.25 -1.02C8.7 6.45 8.5 5.25 8.5 4c0-0.55-0.45-1-1-1z" /> diff --git a/core/res/res/drawable/pocket_mode_img.xml b/core/res/res/drawable/pocket_mode_img.xml new file mode 100644 index 000000000000..81bb727f0bd3 --- /dev/null +++ b/core/res/res/drawable/pocket_mode_img.xml @@ -0,0 +1,30 @@ + + + + + + + diff --git a/core/res/res/drawable/stat_sys_data_usb.xml b/core/res/res/drawable/stat_sys_data_usb.xml new file mode 100644 index 000000000000..fb1ad2a47706 --- /dev/null +++ b/core/res/res/drawable/stat_sys_data_usb.xml @@ -0,0 +1,24 @@ + + + + diff --git a/core/res/res/drawable/sym_def_app_icon.xml b/core/res/res/drawable/sym_def_app_icon.xml index 129d38a74750..38d91477fbce 100644 --- a/core/res/res/drawable/sym_def_app_icon.xml +++ b/core/res/res/drawable/sym_def_app_icon.xml @@ -1,7 +1,19 @@ + - - - + diff --git a/core/res/res/drawable/sym_def_app_icon_foreground.xml b/core/res/res/drawable/sym_def_app_icon_foreground.xml new file mode 100644 index 000000000000..0a5a334d10f7 --- /dev/null +++ b/core/res/res/drawable/sym_def_app_icon_foreground.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + diff --git a/core/res/res/drawable/tab_selected_holo.xml b/core/res/res/drawable/tab_selected_holo.xml new file mode 100644 index 000000000000..af20790beb5c --- /dev/null +++ b/core/res/res/drawable/tab_selected_holo.xml @@ -0,0 +1,18 @@ + + + + + + + + diff --git a/core/res/res/layout/accessibility_enable_service_warning.xml b/core/res/res/layout/accessibility_enable_service_warning.xml index 01ef10177c5a..84520a28228f 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="@string/config_headlineFontFamilyMedium"/> - - + + - + + - - - - \ No newline at end of file + android:layout_gravity="top|center_horizontal" + android:gravity="center" + android:textColor="?android:attr/textColorTertiary" + android:textAppearance="?android:attr/textAppearanceSmall" + /> + diff --git a/core/res/res/layout/global_actions_silent_mode.xml b/core/res/res/layout/global_actions_silent_mode.xml index a3586232a152..dbdb638a37df 100644 --- a/core/res/res/layout/global_actions_silent_mode.xml +++ b/core/res/res/layout/global_actions_silent_mode.xml @@ -17,26 +17,26 @@ - \ No newline at end of file + diff --git a/core/res/res/layout/pocket_lock_view.xml b/core/res/res/layout/pocket_lock_view.xml new file mode 100644 index 000000000000..53eb560d5a04 --- /dev/null +++ b/core/res/res/layout/pocket_lock_view.xml @@ -0,0 +1,109 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/core/res/res/layout/time_picker_material.xml b/core/res/res/layout/time_picker_material.xml index 75973798219e..ee733f1f39e8 100644 --- a/core/res/res/layout/time_picker_material.xml +++ b/core/res/res/layout/time_picker_material.xml @@ -44,7 +44,7 @@ android:paddingTop="20dp" android:paddingBottom="20dp" android:includeFontPadding="false" - android:fontFamily="sans-serif-medium" + android:fontFamily="@string/config_bodyFontFamilyMedium" android:textSize="34sp" android:textColor="@color/white" android:text="@string/time_picker_header_text"/> diff --git a/core/res/res/mipmap-hdpi/sym_def_app_icon_foreground.png b/core/res/res/mipmap-hdpi/sym_def_app_icon_foreground.png deleted file mode 100644 index 4e526c95b4d6..000000000000 Binary files a/core/res/res/mipmap-hdpi/sym_def_app_icon_foreground.png and /dev/null differ diff --git a/core/res/res/mipmap-mdpi/sym_def_app_icon_foreground.png b/core/res/res/mipmap-mdpi/sym_def_app_icon_foreground.png deleted file mode 100644 index 2c38c719088d..000000000000 Binary files a/core/res/res/mipmap-mdpi/sym_def_app_icon_foreground.png and /dev/null differ diff --git a/core/res/res/mipmap-xhdpi/sym_def_app_icon_foreground.png b/core/res/res/mipmap-xhdpi/sym_def_app_icon_foreground.png deleted file mode 100644 index 072467eaaafb..000000000000 Binary files a/core/res/res/mipmap-xhdpi/sym_def_app_icon_foreground.png and /dev/null differ diff --git a/core/res/res/mipmap-xxhdpi/sym_def_app_icon_foreground.png b/core/res/res/mipmap-xxhdpi/sym_def_app_icon_foreground.png deleted file mode 100644 index 78a6b7a3464d..000000000000 Binary files a/core/res/res/mipmap-xxhdpi/sym_def_app_icon_foreground.png and /dev/null differ diff --git a/core/res/res/mipmap-xxxhdpi/sym_def_app_icon_foreground.png b/core/res/res/mipmap-xxxhdpi/sym_def_app_icon_foreground.png deleted file mode 100644 index 68ebe33fe237..000000000000 Binary files a/core/res/res/mipmap-xxxhdpi/sym_def_app_icon_foreground.png and /dev/null differ diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml index 8fb09fc31834..74ff08fd0db0 100644 --- a/core/res/res/values-af/strings.xml +++ b/core/res/res/values-af/strings.xml @@ -663,8 +663,6 @@ "Kan nie jou gesigmodel skep nie. Probeer weer." "Donkerbril bespeur. Jou gesig moet heeltemal sigbaar wees." "Gesigbedekking bespeur. Jou gesig moet heeltemal sigbaar wees." - - "Kan nie gesig verifieer nie. Hardeware nie beskikbaar nie." "Probeer Gesigslot weer" "Kan nie nuwe gesigdata berg nie. Vee eers \'n ou een uit." @@ -682,8 +680,6 @@ "Gebruik Gesigslot of Skermslot" "Gebruik jou gesig om voort te gaan" "Gebruik jou gesig of skermslot om voort te gaan" - - "Iets is fout. Probeer weer." "Gesig-ikoon" "lees sinkroniseer-instellings" diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml index f47cf3fd77bd..b44e452f7a60 100644 --- a/core/res/res/values-am/strings.xml +++ b/core/res/res/values-am/strings.xml @@ -663,8 +663,6 @@ "የመልክዎን ሞዴል መፍጠር አልተቻለም። እንደገና ይሞክሩ።" "ጠቆር ያሉ መነጽሮች ተገኝተዋል። መልክዎ ሙሉ በሙሉ መታየት አለበት።" "የመልክ መሸፈኛ ተገኝቷል። መልክዎ ሙሉ በሙሉ መታየት አለበት።" - - "መልክን ማረጋገጥ አይቻልም። ሃርድዌር የለም።" "በመልክ መክፈትን እንደገና ይሞክሩ" "አዲስ የመልክ ውውሂብ ማስቀመጥ አልተቻለም። መጀመሪያ የድሮውን ይሰርዙት።" @@ -682,8 +680,6 @@ "የመልክ ወይም የማያ ገጽ መቆለፊያን ይጠቀሙ" "ለመቀጠል መልክዎን ይጠቀሙ" "ለመቀጠል መልክዎን ወይም የማያ ገጽዎን መቆለፊያ ይጠቀሙ" - - "የሆነ ችግር ተፈጥሯል። እንደገና ይሞክሩ።" "የፊት አዶ" "የሥምሪያ ቅንብሮች አንብብ" diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml index 8d47c17704bc..6b304597212e 100644 --- a/core/res/res/values-ar/strings.xml +++ b/core/res/res/values-ar/strings.xml @@ -667,8 +667,6 @@ "يتعذّر إنشاء نموذج الوجه. يُرجى إعادة المحاولة." "تمّ رصد نظارة شمسية. يجب أن يكون وجهك ظاهرًا بالكامل." "تمّ رصد قناع على الوجه. يجب أن يكون وجهك ظاهرًا بالكامل." - - "يتعذّر التحقُّق من الوجه. الجهاز غير مُتاح." "جرِّب \"فتح الجهاز بالتعرف على الوجه\" مرة أخرى." "يتعذَّر تخزين بيانات الوجه الجديد. احذف الوجه القديم أولاً." @@ -686,8 +684,6 @@ "استخدام ميزة \"فتح الجهاز بالتعرف على الوجه\" أو ميزة \"قفل الشاشة\"" "استخدِم الوجه للمتابعة" "استخدام ميزة \"فتح القفل بالوجه\" أو ميزة \"قفل الشاشة\" للمتابعة" - - "حدث خطأ، يُرجى إعادة المحاولة." "رمز الوجه" "قراءة إعدادات المزامنة" diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml index f5fbba9a7b7a..5d544b9f8bd1 100644 --- a/core/res/res/values-as/strings.xml +++ b/core/res/res/values-as/strings.xml @@ -663,8 +663,6 @@ "মুখাৱয়বৰ মডেল সৃষ্টি কৰিব নোৱাৰি। পুনৰ চেষ্টা কৰক।" "ডাঠ ৰঙৰ চশমা চিনাক্ত কৰা হৈছে। আপোনাৰ মুখাৱয়ব সম্পূৰ্ণৰূপে দেখা পোৱা হৈ থাকিবই লাগিব।" "মুখাৱয়বত আৱৰণ চিনাক্ত কৰা হৈছে। আপোনাৰ মুখাৱয়ব সম্পূৰ্ণৰূপে দেখা পোৱা হৈ থাকিবই লাগিব।" - - "মুখমণ্ডল সত্যাপন কৰিব পৰা নগ’ল। হাৰ্ডৱেৰ নাই।" "ফেচ আনলক পুনৰ ব্যৱহাৰ কৰি চাওক" "নতুন মুখমণ্ডলৰ ডেটা জমা কৰিব পৰা নাই। প্ৰথমে পুৰণি এখন মচক।" @@ -682,8 +680,6 @@ "ফেচ আনলক অথবা স্ক্ৰীন লক ব্যৱহাৰ কৰক" "অব্যাহত ৰাখিবলৈ নিজৰ মুখাৱয়ব ব্যৱহাৰ কৰক" "অব্যাহত ৰাখিবলৈ আপোনাৰ মুখাৱয়ব অথবা স্ক্ৰীন লক ব্যৱহাৰ কৰক" - - "কিবা ভুল হ’ল। পুনৰ চেষ্টা কৰক।" "মুখমণ্ডলৰ আইকন" "ছিংকৰ ছেটিং পঢ়ক" diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml index bc6aa16ba19f..02de906151dd 100644 --- a/core/res/res/values-az/strings.xml +++ b/core/res/res/values-az/strings.xml @@ -663,8 +663,6 @@ "Üz modelinizi yaratmaq olmur. Yenə cəhd edin." "Tünd eynək aşkar edildi. Üzünüz tam görünməlidir." "Üz örtüyü aşkar edildi. Üzünüz tam görünməlidir." - - "Üz doğrulanmadı. Avadanlıq əlçatan deyil." "Üz ilə kiliddən çıxarmanı yenidən sınayın" "Yeni üz datası saxlanmadı. Əvvəlcə köhnə olanı silin." @@ -682,8 +680,6 @@ "Üz və ya ekran kilidindən istifadə edin" "Davam etmək üçün üzünüzdən istifadə edin" "Davam etmək üçün üz və ya ekran kilidinizdən istifadə edin" - - "Xəta oldu. Yenə cəhd edin." "Üz işarəsi" "sinx ayarlarını oxu" diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml index d0e2a3c13e7b..10f007e9e9d3 100644 --- a/core/res/res/values-b+sr+Latn/strings.xml +++ b/core/res/res/values-b+sr+Latn/strings.xml @@ -664,8 +664,6 @@ "Pravljenje modela lica nije uspelo. Probajte ponovo." "Otkrivene su tamne naočari. Lice mora da bude potpuno vidljivo." "Otkriveno je prekrivanje lica. Lice mora da bude potpuno vidljivo." - - "Provera lica nije uspela. Hardver nije dostupan." "Probajte ponovo otključavanje licem" "Novi podaci o licu nisu sačuvani. Prvo izbrišete prethodne." @@ -683,8 +681,6 @@ "Koristite zaključavanje licem ili zaključavanje ekrana" "Potvrdite identitet licem da biste nastavili" "Koristite lice ili zaključavanje ekrana da biste nastavili" - - "Došlo je do problema. Probajte ponovo." "Ikona lica" "čitanje podešavanja sinhronizacije" diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml index b3e4fc3fd2e9..a5ed30089d2b 100644 --- a/core/res/res/values-be/strings.xml +++ b/core/res/res/values-be/strings.xml @@ -665,8 +665,6 @@ "Не ўдалося стварыць мадэль твару. Паўтарыце спробу." "Выяўлены цёмныя акуляры. Твар павінен быць цалкам бачным." "Нешта засланяе твар. Твар павінен быць цалкам бачным." - - "Твар не спраўджаны. Абсталяванне недаступнае." "Выканайце распазнаванне твару паўторна" "Новыя даныя пра твар не захаваны. Спачатку выдаліце старыя." @@ -684,8 +682,6 @@ "Выкарыстоўваць распазнаванне твару ці блакіроўку экрана" "Каб працягнуць, скарыстайце распазнаванне твару" "Каб працягнуць, скарыстайце распазнаванне твару ці сродак разблакіроўкі экрана" - - "Нешта пайшло не так. Паўтарыце спробу." "Значок твару" "чытаць параметры сінхранізацыі" diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml index 7d21ba3eb29a..98fe6714b2a4 100644 --- a/core/res/res/values-bg/strings.xml +++ b/core/res/res/values-bg/strings.xml @@ -663,8 +663,6 @@ "Моделът на лицето ви не бе създаден. Опитайте отново." "Изглежда, че носите тъмни очила. То трябва да е напълно видимо." "Изглежда, че лицето ви е покрито. То трябва да е напълно видимо." - - "Лицето не може да се потвърди. Хардуерът не е налице." "Опитайте отново да отключите с лице" "Не може да се запази ново лице. Първо изтрийте старо." @@ -682,8 +680,6 @@ "Използване на отключването с лице или опцията за заключване на екрана" "Използвайте лицето си, за да продължите" "Използвайте лицето си или опцията за заключване на екрана, за да продължите" - - "Нещо се обърка. Опитайте отново." "Икона на лице" "четене на настройките за синхронизиране" diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml index 6da6267e5b03..cf47e85fe71b 100644 --- a/core/res/res/values-bn/strings.xml +++ b/core/res/res/values-bn/strings.xml @@ -663,8 +663,6 @@ "ফেস মডেল তৈরি করা যাচ্ছে না। আবার চেষ্টা করুন।" "কালো চশমা শনাক্ত করা হয়েছে। আপনার মুখ পুরোপুরি দৃশ্যমান হতে হবে।" "মুখে মাস্ক শনাক্ত করা হয়েছে। আপনার মুখ পুরোপুরি দৃশ্যমান হতে হবে।" - - "ফেস যাচাই করা যায়নি। হার্ডওয়্যার উপলভ্য নেই।" "\'ফেস আনলক\' আবার ব্যবহার করার চেষ্টা করুন" "নতুন ফেস ডেটা স্টোর করা যায়নি। প্রথমে পুরনোটি মুছে ফেলুন।" @@ -682,8 +680,6 @@ "ফেস অথবা স্ক্রিন লক ব্যবহার করুন" "চালিয়ে যেতে আপনার মুখ ব্যবহার করুন" "চালিয়ে যেতে আপনার ফেস বা স্ক্রিন লক ব্যবহার করুন" - - "কোনও সমস্যা হয়েছে। আবার করে দেখুন।" "ফেস আইকন" "সিঙ্ক সেটিংস পড়ে" diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml index e59733079b44..a036a14d2e78 100644 --- a/core/res/res/values-bs/strings.xml +++ b/core/res/res/values-bs/strings.xml @@ -664,8 +664,6 @@ "Nije moguće kreirati model lica. Pokušajte ponovo." "Otkrivene su tamne naočale. Lice se mora u potpunosti vidjeti." "Otkriveno je pokrivalo preko lica. Lice se mora u potpunosti vidjeti." - - "Nije moguće potvrditi lice. Hardver nije dostupan." "Pokušajte ponovo s otključavanjem licem" "Nije moguće sačuvati nove podatke o licu. Prvo izbrišite stare." @@ -683,8 +681,6 @@ "Koristi otključavanje licem ili zaključavanje ekrana" "Koristite lice da nastavite" "Koristite lice ili zaključavanje ekrana da nastavite" - - "Nešto nije uredu. Pokušajte ponovo." "Ikona lica" "čitanje postavki za sinhroniziranje" diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml index a5ed4e9674ad..c57f0850150b 100644 --- a/core/res/res/values-ca/strings.xml +++ b/core/res/res/values-ca/strings.xml @@ -663,8 +663,6 @@ "No es pot crear el model facial. Torna-ho a provar." "S\'han detectat ulleres fosques. La cara ha de ser completament visible." "S\'ha detectat una mascareta. La cara ha de ser completament visible." - - "No es pot verificar la cara. Maquinari no disponible." "Torna a provar Desbloqueig facial" "No es poden desar dades facials noves. Suprimeix-ne d\'antigues." @@ -682,8 +680,6 @@ "Utilitza el desbloqueig facial o de pantalla" "Utilitza la teva cara per continuar" "Utilitza la cara o el bloqueig de pantalla per continuar" - - "S\'ha produït un error. Torna-ho a provar." "Icona facial" "llegir la configuració de sincronització" diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml index 21b147d81433..3466ea15e3ef 100644 --- a/core/res/res/values-cs/strings.xml +++ b/core/res/res/values-cs/strings.xml @@ -665,8 +665,6 @@ "Model se nepodařilo vytvořit. Zkuste to znovu." "Byly zjištěny tmavé brýle. Obličej musí být plně viditelný." "Byl zjištěn zakrytý obličej. Obličej musí být plně viditelný." - - "Obličej nelze ověřit. Hardware není dostupný." "Zopakujte odemknutí obličejem" "Údaje o novém obličeji nelze uložit. Nejdřív vymažte starý." @@ -684,8 +682,6 @@ "Použít odemknutí obličejem nebo zámek obrazovky" "Pokračujte ověřením obličeje" "Pokračujte ověřením pomocí obličeje nebo zámku obrazovky" - - "Došlo k chybě. Zkuste to znovu." "Ikona obličeje" "čtení nastavení synchronizace" diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml index b04d8c9c1115..c8de8256bb46 100644 --- a/core/res/res/values-da/strings.xml +++ b/core/res/res/values-da/strings.xml @@ -663,8 +663,6 @@ "Din ansigtsmodel kan ikke oprettes. Prøv igen." "Mørke briller er registreret. Dit ansigt skal være helt synligt." "Ansigtsdækning er registreret. Dit ansigt skal være helt synligt." - - "Ansigt ikke bekræftet. Hardware ikke tilgængelig." "Prøv ansigtslås igen" "Der kan ikke gemmes nye ansigtsdata. Slet et gammelt først." @@ -682,8 +680,6 @@ "Brug ansigts- eller skærmlås" "Brug dit ansigt for at fortsætte" "Brug din ansigts- eller skærmlås for at fortsætte" - - "Noget gik galt. Prøv igen." "Ansigt" "læse indstillinger for synkronisering" diff --git a/core/res/res/values-de/bootleg_strings.xml b/core/res/res/values-de/bootleg_strings.xml new file mode 100644 index 000000000000..c41c67aaf911 --- /dev/null +++ b/core/res/res/values-de/bootleg_strings.xml @@ -0,0 +1,30 @@ + + + + + + Bootloader-Neustart + Neustart in Bootloader-Modus\u2026 + Recovery-Neustart + Neustart in Recovery-Modus\u2026 + System-Neustart + System-Neustart + + + diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml index dc9d746459cd..ab68c5fa2119 100644 --- a/core/res/res/values-de/strings.xml +++ b/core/res/res/values-de/strings.xml @@ -663,8 +663,6 @@ "Dein Gesichtsmodell kann nicht erstellt werden. Versuche es noch einmal." "Dunkle Brille erkannt. Dein Gesicht muss vollständig sichtbar sein." "Dein Gesicht ist bedeckt. Es muss vollständig sichtbar sein." - - "Gesicht nicht erkannt. Hardware nicht verfügbar." "Versuche es noch mal mit der Gesichtsentsperrung" "Kein Speicherplatz frei. Bitte erst ein Gesicht löschen." @@ -682,8 +680,6 @@ "Entsperrung per Gesichtserkennung oder Displaysperre verwenden" "Gesichtserkennung verwenden, um fortzufahren" "Verwende die Gesichtserkennung oder deine Display-Entsperrmethode, um fortzufahren" - - "Ein Problem ist aufgetreten. Versuch es noch einmal." "Gesichtssymbol" "Synchronisierungseinstellungen lesen" diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml index 1fcf94e73024..4a9540728198 100644 --- a/core/res/res/values-el/strings.xml +++ b/core/res/res/values-el/strings.xml @@ -663,8 +663,6 @@ "Αδύνατη η δημιουργία του μοντέλου προσώπου. Δοκιμάστε ξανά." "Ανιχνεύτηκαν σκούρα γυαλιά. Το πρόσωπό σας πρέπει να φαίνεται πλήρως." "Ανιχνεύτηκε κάλυμμα προσώπου. Το πρόσωπό σας πρέπει να φαίνεται πλήρως." - - "Αδύν. επαλήθ. προσώπου. Μη διαθέσιμος εξοπλισμός." "Δοκιμάστε ξανά το Ξεκλείδωμα με το πρόσωπο" "Η αποθήκ. νέων δεδομ. προσώπ. είναι αδύν. Διαγρ. ένα παλιό." @@ -682,8 +680,6 @@ "Χρήση προσώπου ή κλειδώματος οθόνης" "Χρησιμοποιήστε το πρόσωπό σας για να συνεχίσετε" "Χρησιμοποιήστε το πρόσωπό σας ή το κλείδωμα οθόνης για συνέχεια" - - "Παρουσιάστηκε κάποιο πρόβλημα. Δοκιμάστε ξανά." "Εικ. προσ." "διαβάζει τις ρυθμίσεις συγχρονισμού" diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml index bde1fe019faf..6e174ee3ec5b 100644 --- a/core/res/res/values-en-rAU/strings.xml +++ b/core/res/res/values-en-rAU/strings.xml @@ -663,8 +663,6 @@ "Can’t create your face model. Try again." "Dark glasses detected. Your face must be fully visible." "Face covering detected. Your face must be fully visible." - - "Can’t verify face. Hardware not available." "Try Face Unlock again" "Can’t store new face data. Delete an old one first." @@ -682,8 +680,6 @@ "Use face or screen lock" "Use your face to continue" "Use your face or screen lock to continue" - - "Something went wrong. Try again." "Face icon" "read sync settings" diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml index cbed459307a2..5ae24c24ec48 100644 --- a/core/res/res/values-en-rCA/strings.xml +++ b/core/res/res/values-en-rCA/strings.xml @@ -663,8 +663,6 @@ "Can’t create your face model. Try again." "Dark glasses detected. Your face must be fully visible." "Face covering detected. Your face must be fully visible." - - "Can’t verify face. Hardware not available." "Try Face Unlock again" "Can’t store new face data. Delete an old one first." @@ -682,8 +680,6 @@ "Use face or screen lock" "Use your face to continue" "Use your face or screen lock to continue" - - "Something went wrong. Try again." "Face icon" "read sync settings" diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml index fcd40c541579..dcd815864f8e 100644 --- a/core/res/res/values-en-rGB/strings.xml +++ b/core/res/res/values-en-rGB/strings.xml @@ -663,8 +663,6 @@ "Can’t create your face model. Try again." "Dark glasses detected. Your face must be fully visible." "Face covering detected. Your face must be fully visible." - - "Can’t verify face. Hardware not available." "Try Face Unlock again" "Can’t store new face data. Delete an old one first." @@ -682,8 +680,6 @@ "Use face or screen lock" "Use your face to continue" "Use your face or screen lock to continue" - - "Something went wrong. Try again." "Face icon" "read sync settings" diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml index e583cefc8bd8..37a40ecb7f8f 100644 --- a/core/res/res/values-en-rIN/strings.xml +++ b/core/res/res/values-en-rIN/strings.xml @@ -663,8 +663,6 @@ "Can’t create your face model. Try again." "Dark glasses detected. Your face must be fully visible." "Face covering detected. Your face must be fully visible." - - "Can’t verify face. Hardware not available." "Try Face Unlock again" "Can’t store new face data. Delete an old one first." @@ -682,8 +680,6 @@ "Use face or screen lock" "Use your face to continue" "Use your face or screen lock to continue" - - "Something went wrong. Try again." "Face icon" "read sync settings" diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml index 77a1af62382a..d3c9cc4eafb2 100644 --- a/core/res/res/values-en-rXC/strings.xml +++ b/core/res/res/values-en-rXC/strings.xml @@ -663,8 +663,6 @@ "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‏‏‎‎‏‎‎‎‏‏‏‏‏‏‎‏‏‏‏‏‏‏‏‏‎‏‎‏‎‎‎‎‎‏‏‎‎‏‎‏‏‎‎‎‏‏‏‏‏‏‎‏‎‏‎‎‏‏‎Can’t create your face model. Try again.‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‎‎‎‏‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‏‎‏‏‎‎‎‎‎‏‏‎‏‎‎‏‏‎‎‎‏‎‏‏‎‏‎‏‎‏‏‏‎‏‎‎‎Dark glasses detected. Your face must be fully visible.‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‏‏‎‎‏‎‎‏‏‎‎‏‏‎‎‎‏‎‏‎‏‎‏‏‏‎‎‏‏‏‎‏‎‎‎‎‎‎‎‏‏‎‎‏‏‎‏‎‎‎‏‎‎‎‏‏‎‎Face covering detected. Your face must be fully visible.‎‏‎‎‏‎" - - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‏‎‏‎‎‏‎‎‏‎‎‏‎‎‎‏‎‎‎‏‏‏‏‎‎‎‎‏‎‏‏‏‏‏‎‎‎‎‏‎‏‎‏‏‏‎‏‎‎‏‏‎‏‏‎‎‎‎‎Can’t verify face. Hardware not available.‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‎‎‎‎‎‏‏‏‏‏‏‏‎‎‎‏‎‎‎‎‏‎‎‎‎‎‎‎‎‏‏‎‏‏‎‏‏‏‏‎‎‏‎‏‏‎‎‎‏‏‏‏‎‎‎‏‎‎Try Face Unlock again‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‏‎‎‏‏‎‎‏‏‎‎‎‏‏‎‏‏‏‏‎‏‏‏‎‏‎‏‎‎‏‎‎‎‏‎‏‏‎‏‎‏‏‎‎‎‎‏‎‎‏‎‏‎‏‏‎‏‏‎Can’t store new face data. Delete an old one first.‎‏‎‎‏‎" @@ -682,8 +680,6 @@ "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‏‎‎‎‏‏‏‏‏‏‏‎‎‎‎‏‏‏‏‎‎‏‎‏‏‏‎‎‏‎‎‏‎‏‎‏‏‏‏‎‎‏‎‏‏‎‎‎‎‏‎‎‏‏‎‎‏‎‎Use face or screen lock‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‏‏‏‏‏‎‎‎‎‎‏‎‏‏‎‏‏‏‎‎‏‎‎‎‏‏‎‎‎‎‏‎‏‏‎‎‎‎‎‏‎‎‎‏‎‎‎‎‎‎‎‎‏‎‎‏‎‏‎Use your face to continue‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‎‏‎‏‏‏‏‎‏‎‎‎‏‏‏‏‎‏‎‏‏‏‏‎‏‎‎‏‎‎‎‏‏‎‏‏‎‎‎‏‎‎‏‏‏‎‏‎‎‎‏‏‎‏‎‏‎‎‎Use your face or screen lock to continue‎‏‎‎‏‎" - - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‏‎‏‎‎‎‎‎‏‏‏‏‏‎‏‎‏‎‏‏‎‎‎‎‎‏‏‏‎‏‎‏‏‏‏‎‎‎‎‏‏‏‎‏‎‎‏‏‏‎‏‎‏‏‎‏‏‎‎Something went wrong. Try again.‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‏‎‎‏‏‏‎‏‎‎‎‎‎‏‏‏‏‎‏‏‎‏‎‎‎‏‏‎‏‏‎‎‎‎‎‎‎‎‎‎‎‏‏‏‏‎‎‏‎‏‏‏‏‏‏‎‎‎‎Face icon‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‏‎‏‎‏‏‏‏‏‎‎‏‎‏‏‎‏‎‏‎‎‎‎‏‏‎‎‎‎‏‏‏‎‎‏‏‎‏‏‏‎‎‎‏‏‏‏‎‏‏‏‎‏‏‎‏‎‏‎read sync settings‎‏‎‎‏‎" diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml index 2a3f916dcdec..059b4d1500b2 100644 --- a/core/res/res/values-es-rUS/strings.xml +++ b/core/res/res/values-es-rUS/strings.xml @@ -664,8 +664,6 @@ "No se puede crear modelo de rostro. Vuelve a intentarlo." "Se detectaron lentes oscuros. Tu rostro debe verse completamente." "Se detectó que llevas mascarilla. Tu rostro debe verse completamente." - - "No se verificó el rostro. Hardware no disponible." "Vuelve a probar Desbloqueo facial" "No hay espacio para datos faciales nuevos. Borra uno viejo." @@ -683,8 +681,6 @@ "Usar desbloqueo facial o de pantalla" "Usa el rostro para continuar" "Usa tu rostro o bloqueo de pantalla para continuar" - - "Se produjo un error. Vuelve a intentarlo." "Ícono cara" "leer la configuración de sincronización" diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml index 0969a5fa0fce..5c39aa74d513 100644 --- a/core/res/res/values-es/strings.xml +++ b/core/res/res/values-es/strings.xml @@ -664,8 +664,6 @@ "No se puede crear tu modelo. Inténtalo de nuevo." "Gafas oscuras detectadas. Tu cara se debe poder ver por completo." "Mascarilla detectada. Tu cara se debe poder ver por completo." - - "No se puede verificar. Hardware no disponible." "Vuelve a probar Desbloqueo facial" "Para guardar nuevos datos faciales, borra otros antiguos." @@ -683,8 +681,6 @@ "Usar Desbloqueo facial o Bloqueo de pantalla" "Usa tu cara para continuar" "Usa tu cara o tu bloqueo de pantalla para continuar" - - "Se ha producido un error. Inténtalo de nuevo." "Icono cara" "leer la configuración de sincronización" diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml index e79cacba10eb..becabdc8e4cb 100644 --- a/core/res/res/values-et/strings.xml +++ b/core/res/res/values-et/strings.xml @@ -663,8 +663,6 @@ "Teie näomudelit ei saa luua. Proovige uuesti." "Tuvastati tumedad prillid. Teie nägu peab olema täielikult nähtaval." "Tuvastati nägu kattev ese. Teie nägu peab olema täielikult nähtaval." - - "Nägu ei saa kinnitada. Riistvara pole saadaval." "Proovige näoga avamist uuesti" "Uue näo andmeid ei saa salvestada. Kustutage enne vanad." @@ -682,8 +680,6 @@ "Näoga avamise või ekraaniluku kasutamine" "Jätkamiseks kasutage oma nägu" "Jätkamiseks kasutage oma nägu või ekraanilukku" - - "Midagi läks valesti. Proovige uuesti." "Näoikoon" "loe sünkroonimisseadeid" diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml index 954c683ff3e7..337ef5cef1f4 100644 --- a/core/res/res/values-eu/strings.xml +++ b/core/res/res/values-eu/strings.xml @@ -663,8 +663,6 @@ "Ezin da sortu aurpegi-eredua. Saiatu berriro." "Betaurreko ilunak hauteman dira. Aurpegi osoak egon behar du ikusgai." "Maskara bat hauteman da. Aurpegi osoak egon behar du ikusgai." - - "Ezin da egiaztatu aurpegia. Hardwarea ez dago erabilgarri." "Saiatu berriro aurpegi bidez desblokeatzen" "Ezin dira gorde aurpegiaren datu berriak. Ezabatu zaharrak." @@ -682,8 +680,6 @@ "Erabili aurpegia edo pantailaren blokeoa" "Aurrera egiteko, erabili aurpegia" "Aurrera egiteko, erabili aurpegia edo pantailaren blokeoa" - - "Arazo bat izan da. Saiatu berriro." "Aurpegiaren ikonoa" "irakurri sinkronizazio-ezarpenak" diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml index 424efe1edf6b..ae214c4d098f 100644 --- a/core/res/res/values-fa/strings.xml +++ b/core/res/res/values-fa/strings.xml @@ -663,8 +663,6 @@ "مدل چهره ایجاد نشد. دوباره امتحان کنید." "عینک تیره شناسایی شد. چهره‌تان باید کاملاً نمایان باشد." "پوشش صورت شناسایی شد. چهره‌تان باید کاملاً نمایان باشد." - - "چهره تأیید نشد. سخت‌افزار در دسترس نیست." "«قفل‌گشایی با چهره» را دوباره امتحان کنید" "داده‌ چهره جدید ذخیره نشد. اول داده‌ چهره قدیمی را حذف کنید." @@ -682,8 +680,6 @@ "استفاده از قفل صفحه یا چهره" "برای ادامه، از چهره‌تان استفاده کنید" "برای ادامه، از تشخیص چهره یا قفل صفحه استفاده کنید" - - "مشکلی پیش آمد. دوباره امتحان کنید." "نماد چهره" "خواندن تنظیمات همگام‌سازی" diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml index 3f2b4ceb8d3e..0a09d9cdc15c 100644 --- a/core/res/res/values-fi/strings.xml +++ b/core/res/res/values-fi/strings.xml @@ -663,8 +663,6 @@ "Kasvomallia ei voi luoda. Yritä uudelleen." "Tummat lasit havaittu. Kasvojen täytyy näkyä kokonaan." "Kasvot peittävä asia havaittu. Kasvojen täytyy näkyä kokonaan." - - "Kasvoja ei voi vahvistaa. Laitteisto ei käytettäv." "Yritä käyttää kasvojentunnistusavausta uudelleen" "Uutta kasvodataa ei voi tallentaa. Poista ensin vanhaa." @@ -682,8 +680,6 @@ "Käytä kasvojentunnistusavausta tai näytön lukitusta" "Jatka kasvojesi avulla" "Jatka kasvojentunnistuksen tai näytön lukituksen avulla" - - "Jotain meni vikaan. Yritä uudelleen." "Kasvokuvake" "lue synkronointiasetuksia" diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml index 067388d7798a..df7a298a4f42 100644 --- a/core/res/res/values-fr-rCA/strings.xml +++ b/core/res/res/values-fr-rCA/strings.xml @@ -664,8 +664,6 @@ "Impossible de créer votre modèle facial. Réessayez." "Lunettes sombres détectées. Votre visage doit être entièrement visible." "Couvre-visage détecté. Votre visage doit être entièrement visible." - - "Imposs. de vérif. visage. Matériel non accessible." "Réessayez déverrouillage reconnaissance faciale" "Impossible de stocker de nouveaux visages. Supprimez-en un." @@ -683,8 +681,6 @@ "Utiliser la reconnaissance faciale ou le verrouillage de l\'écran" "Utilisez votre visage pour continuer" "Utilisez votre visage ou le verrouillage de l\'écran pour continuer" - - "Un problème est survenu. Réessayez." "Icône visage" "lire les paramètres de synchronisation" diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml index 204c658c0b63..7e9f088c7fab 100644 --- a/core/res/res/values-fr/strings.xml +++ b/core/res/res/values-fr/strings.xml @@ -664,8 +664,6 @@ "Impossible de créer votre empreinte faciale. Réessayez." "Lunettes sombres détectées. Votre visage doit être entièrement visible." "Masque détecté. Votre visage doit être entièrement visible." - - "Imposs. valider visage. Matériel non disponible." "Réessayez d\'utiliser le déverrouillage facial" "Impossible stocker nouv. visages. Veuillez en supprimer un." @@ -683,8 +681,6 @@ "Utiliser déverrouillage par authent. faciale ou verrouillage écran" "Utilisez la reconnaissance faciale pour continuer" "Utilisez la reconnaissance faciale ou le verrouillage de l\'écran pour continuer" - - "Un problème est survenu. Réessayez." "Icône visage" "lire les paramètres de synchronisation" diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml index b092453bebcf..4fbf81cf21e1 100644 --- a/core/res/res/values-gl/strings.xml +++ b/core/res/res/values-gl/strings.xml @@ -663,8 +663,6 @@ "Non se puido crear o modelo facial. Téntao de novo." "Detectáronse lentes escuras. A cara debe poder verse por completo." "Detectouse unha máscara. A cara debe poder verse por completo." - - "Sen verificar a cara. Hardware non dispoñible." "Tenta utilizar o desbloqueo facial de novo" "Para gardar novos datos faciais, elimina os antigos." @@ -682,8 +680,6 @@ "Utilizar desbloqueo facial ou credencial do dispositivo" "Usa a cara para continuar" "Para continuar, utiliza o desbloqueo facial ou a credencial do dispositivo" - - "Produciuse un erro. Téntao de novo." "Icona cara" "ler a configuración de vinculación" diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml index fa3784fa385a..ce903d8f6992 100644 --- a/core/res/res/values-gu/strings.xml +++ b/core/res/res/values-gu/strings.xml @@ -663,8 +663,6 @@ "તમારા ચહેરાનું મૉડલ ન બનાવી શકાય. ફરી પ્રયાસ કરો." "કાળા ચશ્માંની ભાળ મળી. તમારો આખો ચહેરો દેખાવો આવશ્યક છે." "ચહેરો ઢંકાયેલો હોવાની ભાળ મળી. તમારો આખો ચહેરો દેખાવો આવશ્યક છે." - - "ચહેરો ચકાસી શકાતો નથી. હાર્ડવેર ઉપલબ્ધ નથી." "ફેસ અનલૉકને ફરી અજમાવો" "ચહેરાનો નવો ડેટા સ્ટોર કરી શકતાં નથી. પહેલા જૂનો ડિલીટ કરો." @@ -682,8 +680,6 @@ "ફેસ લૉક અથવા સ્ક્રીન લૉકનો ઉપયોગ કરો" "આગળ વધવા માટે તમારા ચહેરાનો ઉપયોગ કરો" "ચાલુ રાખવા માટે તમારા ફેસ લૉક અથવા સ્ક્રીન લૉકનો ઉપયોગ કરો" - - "કંઈક ખોટું થયું. ફરી પ્રયાસ કરો." "ચહેરા આઇકન" "સિંક સેટિંગ વાંચો" diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml index cb0e30daba2e..c4b2570921fe 100644 --- a/core/res/res/values-hi/strings.xml +++ b/core/res/res/values-hi/strings.xml @@ -663,8 +663,6 @@ "चेहरे का माॅडल नहीं बन सका. फिर से कोशिश करें." "आपने गहरे रंग का चश्मा पहना है. आपका पूरा चेहरा दिखना चाहिए." "चेहरा ढका हुआ है. आपका पूरा चेहरा दिखना चाहिए." - - "चेहरा नहीं पहचान पा रहे. हार्डवेयर उपलब्ध नहीं है." "फ़ेस अनलॉक की सुविधा फिर से आज़माएं" "चेहरे का नया डेटा सेव नहीं हो सकता. कोई पुराना डेटा मिटाएं." @@ -682,8 +680,6 @@ "\'फ़ेस अनलॉक\' या स्क्रीन लॉक का क्रेडेंशियल इस्तेमाल करें" "जारी रखने के लिए, अपने चेहरे की मदद से पुष्टि करें" "जारी रखने के लिए, अपना चेहरा दिखाकर या स्क्रीन लॉक क्रेडेंशियल डालकर पुष्टि करें" - - "कोई गड़बड़ी हुई. फिर से कोशिश करें." "चेहरे का आइकॉन" "समन्वयन सेटिंग पढ़ें" diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml index fb95a1f1acca..a63b56b08376 100644 --- a/core/res/res/values-hr/strings.xml +++ b/core/res/res/values-hr/strings.xml @@ -664,8 +664,6 @@ "Izrada modela lica nije uspjela. Pokušajte ponovo." "Otkrivene su tamne naočale. Vaše lice mora biti potpuno vidljivo." "Otkriveno je prekrivanje lica. Vaše lice mora biti potpuno vidljivo." - - "Lice nije potvrđeno. Hardver nije dostupan." "Ponovo pokušajte otključavanje licem" "Podaci o novom licu nisu pohranjeni. Izbrišite neko staro." @@ -683,8 +681,6 @@ "Upotreba otključavanja licem ili zaključavanja zaslona" "Autentificirajte se licem da biste nastavili" "Za nastavak se identificirajte licem ili vjerodajnicom zaključavanja zaslona" - - "Nešto nije u redu. Pokušajte ponovo." "Ikona lica" "čitanje postavki sinkronizacije" diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml index 6c700dd1179c..ee0f0ed59aa8 100644 --- a/core/res/res/values-hu/strings.xml +++ b/core/res/res/values-hu/strings.xml @@ -663,8 +663,6 @@ "Nem lehet létrehozni az arcmodellt. Próbálja újra." "Sötét szemüveget észlelt a rendszer. Arcának teljesen láthatónak kell lennie." "Valami eltakarja az arcát. Arcának teljesen láthatónak kell lennie." - - "Sikertelen arcellenőrzés. A hardver nem érhető el." "Próbálja újra az Arcalapú feloldást" "Nem tárolhatók újabb arcadatok. Törölje valamelyik arcot." @@ -682,8 +680,6 @@ "A folytatás arcalapú feloldással vagy képernyőzárral lehetséges" "A folytatáshoz használja az arcalapú feloldást" "A folytatás arcalapú feloldással vagy a képernyőzár feloldásával lehetséges" - - "Hiba történt. Próbálja újra." "Arcikon" "szinkronizálási beállítások olvasása" diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml index c335648726eb..5c86cd73c233 100644 --- a/core/res/res/values-hy/strings.xml +++ b/core/res/res/values-hy/strings.xml @@ -663,8 +663,6 @@ "Չհաջողվեց ստեղծել ձեր դեմքի մոդելը։ Նորից փորձեք։" "Հանեք ակնոցը։ Ձեր դեմքը պետք է ամբողջովին տեսանելի լինի։" "Դեմքի մի մասը ծածկված է։ Ձեր դեմքը պետք է ամբողջովին տեսանելի լինի։" - - "Չհաջողվեց հաստատել դեմքը։ Սարքն անհասանելի է:" "Նորից փորձեք դեմքով ապակողպումը" "Չհաջողվեց պահել նոր դեմքը։ Ջնջեք հին տարբերակը։" @@ -682,8 +680,6 @@ "Օգտագործել դեմքով ապակողպում կամ էկրանի կողպում" "Շարունակելու համար օգտագործեք դեմքի նույնականացումը" "Շարունակելու համար օգտագործեք ձեր դեմքը կամ էկրանի կողպումը" - - "Սխալ առաջացավ։ Նորից փորձեք։" "Դեմքի պատկերակ" "կարդալ համաժամացման կարգավորումները" diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml index 405717cbb85f..9c49a6ccd8ba 100644 --- a/core/res/res/values-in/strings.xml +++ b/core/res/res/values-in/strings.xml @@ -663,8 +663,6 @@ "Tidak dapat membuat model wajah Anda. Coba lagi." "Kacamata hitam terdeteksi. Wajah Anda harus terlihat sepenuhnya." "Penutup wajah terdeteksi. Wajah Anda harus terlihat sepenuhnya." - - "Tidak dapat memverifikasi wajah. Hardware tidak tersedia." "Coba Face Unlock lagi" "Tidak dapat menyimpan data wajah. Hapus dahulu data lama." @@ -682,8 +680,6 @@ "Gunakan face lock atau kunci layar" "Gunakan wajah untuk melanjutkan" "Gunakan face lock atau kunci layar untuk melanjutkan" - - "Terjadi error. Coba lagi." "Ikon wajah" "baca setelan sinkronisasi" diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml index 743a21511e9d..7bc698b2ecf9 100644 --- a/core/res/res/values-is/strings.xml +++ b/core/res/res/values-is/strings.xml @@ -663,8 +663,6 @@ "Ekki tekst að búa til andlitslíkan. Reyndu aftur." "Dökk gleraugu greindust. Allt andlitið á þér þarf að sjást." "Eitthvað er fyrir andlitinu. Allt andlitið á þér þarf að sjást." - - "Andlit ekki staðfest. Vélbúnaður er ekki tiltækur." "Prófaðu andlitskenni aftur." "Ekki er hægt að vista ný andlitsgögn. Eyddu gömlu fyrst." @@ -682,8 +680,6 @@ "Nota andlit eða skjálás" "Notaðu andlitið þitt til að halda áfram" "Notaðu andlitið eða skjálás til að halda áfram" - - "Eitthvað fór úrskeiðis. Reyndu aftur." "Andlitstákn" "lesa samstillingar" diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml index 0e32d331241c..2e3da3c8b8d6 100644 --- a/core/res/res/values-it/strings.xml +++ b/core/res/res/values-it/strings.xml @@ -664,8 +664,6 @@ "Impossibile creare il modello del volto. Riprova." "Sono stati rilevati occhiali scuri. Il tuo volto deve essere visibile per intero." "È stata rilevata una mascherina. Il tuo volto deve essere visibile per intero." - - "Imposs. verificare volto. Hardware non disponibile." "Riprova lo sblocco con il volto" "Imposs. salvare dati nuovi volti. Elimina un volto vecchio." @@ -683,8 +681,6 @@ "Usa lo sblocco con il volto o il blocco schermo" "Usa il tuo volto per continuare" "Per continuare devi usare il tuo volto o il tuo blocco schermo" - - "Si è verificato un errore. Riprova." "Icona volto" "lettura impostazioni di sincronizz." diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml index 57176a60fc80..181b004bfa9b 100644 --- a/core/res/res/values-iw/strings.xml +++ b/core/res/res/values-iw/strings.xml @@ -665,8 +665,6 @@ "לא ניתן ליצור את התבנית לזיהוי הפנים. יש לנסות שוב." "זוהו משקפיים כהים. הפנים שלך חייבות להיות גלויות לגמרי." "זוהה כיסוי על הפנים. הפנים שלך חייבות להיות גלויות לגמרי." - - "לא ניתן לאמת את הפנים. החומרה לא זמינה." "יש לנסות שוב את הפתיחה ע\"י זיהוי הפנים" "לא ניתן לאחסן נתוני פנים חדשים. תחילה יש למחוק את הנתונים הישנים." @@ -684,8 +682,6 @@ "שימוש בזיהוי פנים או בנעילת מסך" "יש להשתמש באימות פנים כדי להמשיך" "יש להשתמש בזיהוי הפנים או בנעילת המסך כדי להמשיך" - - "משהו השתבש. עליך לנסות שוב." "סמל הפנים" "קריאת הגדרות הסנכרון" diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml index c48d37cc8d50..c7e39eedf1b8 100644 --- a/core/res/res/values-ja/strings.xml +++ b/core/res/res/values-ja/strings.xml @@ -663,8 +663,6 @@ "顔モデルを作成できません。もう一度お試しください。" "サングラスが検出されました。顔が完全に写るようにしてください。" "マスクが検出されました。顔が完全に写るようにしてください。" - - "顔を確認できません。ハードウェアを利用できません。" "顔認証をもう一度お試しください" "新しい顔データを保存できません。古いデータを削除してください。" @@ -682,8 +680,6 @@ "顔認証または画面ロックの使用" "続行するには顔認証を使用してください" "続行するには、顔認証または画面ロックを使用してください" - - "エラーが発生しました。もう一度お試しください。" "顔アイコン" "同期設定の読み取り" diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml index 9cc6ae48df0b..832fe0055c57 100644 --- a/core/res/res/values-ka/strings.xml +++ b/core/res/res/values-ka/strings.xml @@ -663,8 +663,6 @@ "თქვენი სახის მოდელი ვერ იქმნება. ცადეთ ხელახლა." "აღმოჩენილია მუქი სათვალე. თქვენი სახე მთლიანად უნდა ჩანდეს." "აღმოჩენილია სახის დაფარვა. თქვენი სახე მთლიანად უნდა ჩანდეს." - - "სახე ვერ დასტურდება. აპარატი მიუწვდომელია." "ხელახლა ცადეთ სახით განბლოკვა" "სახის ახალი მონაცემები ვერ ინახება. ჯერ ძველი წაშალეთ." @@ -682,8 +680,6 @@ "გამოიყენეთ სახით ან ეკრანის დაბლოკვა" "გასაგრძელებლად გამოიყენეთ თქვენი სახე" "გასაგრძელებლად გამოიყენეთ თქვენი სახე ან ეკრანის განბლოკვის ნიმუში" - - "რაღაც შეცდომა მოხდა. ცადეთ ხელახლა." "სახის ხატულა" "სინქრონიზაციის პარამეტრების წაკითხვა" diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml index 4c5fe51d548b..12fc46a17d82 100644 --- a/core/res/res/values-kk/strings.xml +++ b/core/res/res/values-kk/strings.xml @@ -663,8 +663,6 @@ "Бет үлгісі жасалмады. Қайталап көріңіз." "Қою түсті көзілдірік анықталды. Бетіңіз толық көрініп тұруы керек." "Бетперде анықталды. Бетіңіз толық көрініп тұруы керек." - - "Бетті тану мүмкін емес. Жабдық қолжетімді емес." "Бет тану функциясын қайта қолданып көріңіз." "Жаңа бетті сақтау мүмкін емес. Алдымен ескісін жойыңыз." @@ -682,8 +680,6 @@ "Face Lock функциясын немесе экран құлпын пайдалану" "Жалғастыру үшін бетіңізді көрсетіңіз." "Жалғастыру үшін бетті анықтау функциясын немесе экран құлпын пайдаланыңыз." - - "Бірдеңе дұрыс болмады. Қайталап көріңіз." "Бет белгішесі" "синх параметрлерін оқу" diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml index eb8f757200f5..29f3d6dd0680 100644 --- a/core/res/res/values-km/strings.xml +++ b/core/res/res/values-km/strings.xml @@ -663,8 +663,6 @@ "មិនអាចបង្កើតគំរូមុខរបស់អ្នកបានទេ។ សូមព្យាយាមម្ដងទៀត។" "បានរកឃើញ​វ៉ែនតាខ្មៅ។ មុខរបស់អ្នកត្រូវតែ​អាចមើលឃើញ​ពេញលេញ។" "បានរកឃើញ​គ្រឿងពាក់លើមុខ។ មុខរបស់អ្នកត្រូវតែ​អាចមើលឃើញ​ពេញលេញ។" - - "មិនអាច​ផ្ទៀងផ្ទាត់​មុខបានទេ។ មិនមាន​ហាតវែរទេ។" "សាកល្បង​ដោះសោតាមទម្រង់មុខ​ម្ដងទៀត" "មិនអាច​ផ្ទុកទិន្នន័យទម្រង់​មុខថ្មី​បានទេ។ សូមលុបទិន្នន័យទម្រង់​មុខចាស់ជាមុនសិន។" @@ -682,8 +680,6 @@ "ប្រើមុខ ឬ​ការចាក់សោអេក្រង់" "ប្រើមុខរបស់អ្នក ដើម្បីបន្ត" "ប្រើការចាក់សោអេក្រង់ ឬមុខរបស់អ្នក ដើម្បីបន្ត" - - "មានអ្វីមួយខុសប្រក្រតី។ សូមព្យាយាមម្ដងទៀត។" "រូប​ផ្ទៃមុខ" "អាន​ការ​កំណត់​ធ្វើ​សម​កាល​កម្ម" diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml index 0c7d4b176247..a69a00eaaefc 100644 --- a/core/res/res/values-kn/strings.xml +++ b/core/res/res/values-kn/strings.xml @@ -663,8 +663,6 @@ "ಫೇಸ್ ಮಾಡೆಲ್ ರಚಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ. ಪುನಃ ಪ್ರಯತ್ನಿಸಿ." "ಕಪ್ಪು ಕನ್ನಡಕ ಪತ್ತೆಯಾಗಿದೆ. ನಿಮ್ಮ ಮುಖವು ಸಂಪೂರ್ಣವಾಗಿ ಗೋಚರಿಸಬೇಕು." "ಮುಖವಾಡ ಪತ್ತೆಯಾಗಿದೆ. ನಿಮ್ಮ ಮುಖವು ಸಂಪೂರ್ಣವಾಗಿ ಗೋಚರಿಸಬೇಕು." - - "ಮುಖ ದೃಢೀಕರಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ. ಹಾರ್ಡ್‌ವೇರ್ ಲಭ್ಯವಿಲ್ಲ." "ಫೇಸ್ ಅನ್‌ಲಾಕ್ ಅನ್ನು ಪುನಃ ಪ್ರಯತ್ನಿಸಿ" "ಹೊಸ ಮುಖ ಡೇಟಾ ಸಂಗ್ರಹಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ. ಮೊದಲು ಹಳೆಯದನ್ನು ಅಳಿಸಿ" @@ -682,8 +680,6 @@ "ಫೇಸ್ ಅಥವಾ ಸ್ಕ್ರೀನ್ ಲಾಕ್ ಅನ್ನು ಬಳಸಿ" "ಮುಂದುವರಿಸಲು ನಿಮ್ಮ ಮುಖವನ್ನು ಬಳಸಿ" "ಮುಂದುವರಿಸಲು ನಿಮ್ಮ ಮುಖ ಅಥವಾ ಸ್ಕ್ರೀನ್ ಲಾಕ್ ಅನ್ನು ಬಳಸಿ" - - "ಏನೋ ತಪ್ಪಾಗಿದೆ. ಪುನಃ ಪ್ರಯತ್ನಿಸಿ." "ಮುಖದ ಐಕಾನ್‌" "ಸಿಂಕ್ ಸೆಟ್ಟಿಂಗ್‌ಗಳನ್ನು ರೀಡ್‌ ಮಾಡು" diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml index 82ab2629cb8a..e68611b04f66 100644 --- a/core/res/res/values-ko/strings.xml +++ b/core/res/res/values-ko/strings.xml @@ -663,8 +663,6 @@ "얼굴 모델을 만들 수 없습니다. 다시 시도해 주세요." "선글라스가 감지되었습니다. 전체 얼굴이 보여야 합니다." "마스크가 감지되었습니다. 전체 얼굴이 보여야 합니다." - - "얼굴을 확인할 수 없습니다. 하드웨어를 사용할 수 없습니다." "얼굴 인식 잠금 해제를 다시 시도해 주세요." "새 얼굴 데이터를 저장할 수 없습니다. 먼저 기존 얼굴 데이터를 삭제하세요." @@ -682,8 +680,6 @@ "얼굴 또는 화면 잠금 사용" "계속하려면 얼굴로 인증하세요" "계속하려면 얼굴 또는 화면 잠금을 사용하세요" - - "문제가 발생했습니다. 다시 시도해 보세요." "얼굴 아이콘" "동기화 설정 읽기" diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml index cdbad1854918..5a37f52d9244 100644 --- a/core/res/res/values-ky/strings.xml +++ b/core/res/res/values-ky/strings.xml @@ -663,8 +663,6 @@ "Жүзүңүздүн үлгүсү түзүлгөн жок. Кайталаңыз." "Кара көз айнек кийгенге болбойт. Жүзүңүз толук көрүнүшү керек." "Жүзүңүз жабылып калды. Ал толук көрүнүшү керек." - - "Жүз ырасталбай жатат. Аппараттык камсыздоо жеткиликсиз." "Жүзүнөн таанып ачуу функциясын кайрадан колдонуңуз" "Жаңы жүздү сактоо мүмкүн эмес. Адегенде эскисин өчүрүңүз." @@ -682,8 +680,6 @@ "Жүзүнөн таанып ачууну же экрандын кулпусун колдонуу" "Улантуу үчүн жүзүңүздү көрсөтүңүз" "Улантуу үчүн жүзүңүздү же экрандын кулпусун колдонуңуз" - - "Бир жерден ката кетти. Кайра аракет кылыңыз." "Жүздүн сүрөтчөсү" "шайкештирүү жөндөөлөрүн окуу" diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml index 81ec5b7054c0..a62d11606caa 100644 --- a/core/res/res/values-lo/strings.xml +++ b/core/res/res/values-lo/strings.xml @@ -663,8 +663,6 @@ "ບໍ່ສາມາດສ້າງຮູບແບບໃບໜ້າຂອງທ່ານໄດ້. ກະລຸນາລອງໃໝ່." "ກວດພົບແວ່ນຕາດຳ. ໃບໜ້າຂອງທ່ານຕ້ອງສະແດງໃຫ້ເຫັນໝົດ." "ກວດພົບການປົກປິດໃບໜ້າ. ໃບໜ້າຂອງທ່ານຕ້ອງສະແດງໃຫ້ເຫັນໝົດ." - - "ບໍ່ສາມາດຢັ້ງຢືນໃບໜ້າໄດ້. ບໍ່ມີຮາດແວໃຫ້ໃຊ້." "ກະລຸນາລອງປົດລັອກດ້ວຍໜ້າອີກເທື່ອໜຶ່ງ" "ບໍ່ສາມາດບັນທຶກຂໍ້ມູນໃບໜ້າໃໝ່ໄດ້. ກະລຸນາລຶບຂໍ້ມູນເກົ່າອອກກ່ອນ." @@ -682,8 +680,6 @@ "ໃຊ້ໃບໜ້າ ຫຼື ການລັອກໜ້າຈໍ" "ໃຊ້ໜ້າທ່ານເພື່ອສືບຕໍ່" "ໃຊ້ໃບໜ້າ ຫຼື ການລັອກໜ້າຈໍຂອງທ່ານເພື່ອດຳເນີນການຕໍ່" - - "ມີບາງຢ່າງຜິດພາດເກີດຂຶ້ນ. ກະລຸນາລອງໃໝ່." "ໄອຄອນໃບໜ້າ" "ອ່ານການຕັ້ງຄ່າຊິ້ງຂໍ້ມູນ" diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml index 004a0a8fbb7a..d63fa55eb0ac 100644 --- a/core/res/res/values-lt/strings.xml +++ b/core/res/res/values-lt/strings.xml @@ -665,8 +665,6 @@ "Nepavyko sukurti veido modelio. Band. dar kartą." "Aptikti akiniai nuo saulės. Visas veidas turi būti matomas." "Aptikta veido kaukė. Visas veidas turi būti matomas." - - "Nepavyko patv. veido. Aparatinė įranga negalima." "Bandykite dar kartą naudoti atrakinimą pagal veidą" "Nepavyko išs. naujų veido duomenų. Pirm. ištrinkite senus." @@ -684,8 +682,6 @@ "Naudoti atrakinimą pagal veidą arba ekrano užraktą" "Jei norite tęsti, naudokite atpažinimą pagal veidą" "Jei norite tęsti, naudokite veido atpažinimo funkciją arba ekrano užraktą" - - "Kažkas nepavyko. Bandykite dar kartą." "Veido pkt." "skaityti sinchronizavimo nustatymus" diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml index ef1a95f616c4..51320747b484 100644 --- a/core/res/res/values-lv/strings.xml +++ b/core/res/res/values-lv/strings.xml @@ -664,8 +664,6 @@ "Nevar izveidot sejas modeli. Mēģiniet vēlreiz." "Konstatētas tumšas brilles. Sejai ir jābūt pilnībā redzamai." "Konstatēts sejas aizsegs. Sejai ir jābūt pilnībā redzamai." - - "Nevar verificēt seju. Aparatūra nav pieejama." "Vēlreiz mēģiniet veikt autorizāciju pēc sejas." "Nevar saglabāt jaunās sejas datus. Dzēsiet kādu no vecajām." @@ -683,8 +681,6 @@ "Autorizācijas pēc sejas vai ekrāna bloķēšanas metodes izmantošana" "Lai turpinātu, veiciet autorizāciju pēc sejas" "Izmantojiet autorizāciju pēc sejas vai ekrāna bloķēšanas opciju, lai turpinātu" - - "Radās kļūda. Mēģiniet vēlreiz." "Sejas ikona" "lasīt sinhronizācijas iestatījumus" diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml index 667f5023fc79..5100a29fe64c 100644 --- a/core/res/res/values-mk/strings.xml +++ b/core/res/res/values-mk/strings.xml @@ -663,8 +663,6 @@ "Не може да создаде модел на лик. Обидете се пак." "Носите темни очила. Лицето мора да ви се гледа целосно." "Лицето е покриено. Лицето мора да ви се гледа целосно." - - "Ликот не може да се потврди. Хардвер - недостапен." "Пробајте „Отклучување со лик“ повторно" "Не се зачуваа податоците за нов лик. Избришете го стариот." @@ -682,8 +680,6 @@ "Користи лик или заклучување екран" "Користете го вашиот лик за да продолжите" "Користете отклучување со лик или заклучување екран за да продолжите" - - "Нешто не е во ред. Обидете се повторно." "Икона" "чита поставки за синхронизација" diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml index 4bd200125a80..255b72774d3a 100644 --- a/core/res/res/values-ml/strings.xml +++ b/core/res/res/values-ml/strings.xml @@ -663,8 +663,6 @@ "മുഖ മോഡൽ സൃഷ്ടിക്കാനാകില്ല. വീണ്ടും ശ്രമിക്കൂ." "കറുത്ത കണ്ണട കണ്ടെത്തി. നിങ്ങളുടെ മുഖം പൂർണ്ണമായും ദൃശ്യമായിരിക്കണം." "മുഖം മറച്ചിരിക്കുന്നതായി കണ്ടെത്തി. നിങ്ങളുടെ മുഖം പൂർണ്ണമായും ദൃശ്യമായിരിക്കണം." - - "മുഖം പരിശോധിക്കാൻ കഴിയില്ല. ഹാർഡ്‌വെയർ ലഭ്യമല്ല." "ഫെയ്‌സ് അൺലോക്ക് വീണ്ടും പരീക്ഷിച്ച് നോക്കൂ" "പുതിയ മുഖ ഡാറ്റ സംഭരിക്കാനാകില്ല. ആദ്യം പഴയത് ഇല്ലാതാക്കുക." @@ -682,8 +680,6 @@ "ഫെയ്‌സ് അല്ലെങ്കിൽ സ്‌ക്രീൻ ലോക്ക് ഉപയോഗിക്കുക" "തുടരാൻ നിങ്ങളുടെ മുഖം ഉപയോഗിക്കുക" "തുടരാൻ നിങ്ങളുടെ മുഖം‌ അല്ലെങ്കിൽ സ്‌ക്രീൻ ലോക്ക് ഉപയോഗിക്കുക" - - "എന്തോ കുഴപ്പമുണ്ടായി. വീണ്ടും ശ്രമിക്കുക." "മുഖത്തിന്റെ ഐക്കൺ" "സമന്വയ ക്രമീകരണങ്ങൾ റീഡുചെയ്യുക" diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml index 1b92252f2b47..c6de1844b3d5 100644 --- a/core/res/res/values-mn/strings.xml +++ b/core/res/res/values-mn/strings.xml @@ -663,8 +663,6 @@ "Нүүрний загвар үүсгэж чадсангүй. Дахин оролдоно уу." "Хар шил илэрлээ. Таны нүүр бүтэн харагдах ёстой." "Нүүрний халхавч илэрлээ. Таны нүүр бүтэн харагдах ёстой." - - "Царайг бататгаж чадсангүй. Техник хангамж боломжгүй байна." "Царайгаар түгжээ тайлахыг дахин оролдоно уу" "Царайн шинэ өгөгдлийг хадгалж чадсангүй. Эхлээд хуучин өгөгдлийг устгана уу." @@ -682,8 +680,6 @@ "Царайгаар түгжээ тайлах эсвэл дэлгэцийн түгжээ ашиглах" "Үргэлжлүүлэхийн тулд царайгаа ашиглана уу" "Үргэлжлүүлэхийн тулд царай эсвэл дэлгэцийн түгжээгээ ашиглана уу" - - "Алдаа гарлаа. Дахин оролдоно уу." "Царайны дүрс тэмдэг" "синк тохиргоог унших" diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml index fd7ce2ef9b06..d28c40206d9a 100644 --- a/core/res/res/values-mr/strings.xml +++ b/core/res/res/values-mr/strings.xml @@ -663,8 +663,6 @@ "फेस मॉडेल तयार करू शकत नाही. पुन्हा प्रयत्न करा." "गडद चष्मा डिटेक्ट केला. तुमचा चेहरा पूर्णपणे दृश्यमान असणे आवश्यक आहे." "चेहर्‍यावरील आच्छादन डिटेक्ट केले. तुमचा चेहरा पूर्णपणे दृश्यमान असणे आवश्यक आहे." - - "चेहरा पडताळू शकत नाही. हार्डवेअर उपलब्ध नाही." "फेस अनलॉक वापरण्याचा पुन्हा प्रयत्न करा" "नवीन फेस डेटा स्टोअर करू शकत नाही. आधी जुना हटवा." @@ -682,8 +680,6 @@ "फेस किंवा स्क्रीन लॉक वापरा" "पुढे सुरू ठेवण्यासाठी तुमचा चेहरा वापरा" "पुढे सुरू ठेवण्यासाठी तुमचा चेहरा किंवा स्क्रीन लॉक वापरा" - - "काहीतरी चूक झाली. पुन्हा प्रयत्न करा." "चेहरा आयकन" "सिंक सेटिंग्‍ज वाचा" diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml index 5a623be45dbd..20fea67e090e 100644 --- a/core/res/res/values-ms/strings.xml +++ b/core/res/res/values-ms/strings.xml @@ -663,8 +663,6 @@ "Tidak dapat membuat model wajah anda. Cuba lagi." "Cermin mata gelap dikesan. Wajah anda mesti terlihat sepenuhnya." "Pelitup muka dikesan. Wajah anda mesti terlihat sepenuhnya." - - "Tdk dpt sahkan wajah. Perkakasan tidak tersedia." "Cuba Buka Kunci Wajah sekali lagi" "Tdk dpt menyimpan data wajah baharu. Padamkan yg lama dahulu." @@ -682,8 +680,6 @@ "Gunakan kunci wajah atau skrin" "Gunakan wajah untuk teruskan" "Gunakan wajah atau kunci skrin anda untuk meneruskan" - - "Kesilapan telah berlaku. Cuba lagi." "Ikon wajah" "membaca tetapan penyegerakan" diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml index abb274023b3b..71b6f1fa4b2b 100644 --- a/core/res/res/values-my/strings.xml +++ b/core/res/res/values-my/strings.xml @@ -663,8 +663,6 @@ "သင့်မျက်နှာနမူနာ ပြုလုပ်၍မရပါ။ ထပ်စမ်းကြည့်ပါ။" "အရောင်ရင့်သောမျက်မှန် တွေ့သည်။ သင့်မျက်နှာကို အပြည့်အဝ မြင်ရရန်လိုအပ်သည်။" "မျက်နှာဖုံး တွေ့သည်။ သင့်မျက်နှာကို အပြည့်အဝ မြင်ရရန်လိုအပ်သည်။" - - "မျက်နှာကို အတည်ပြု၍ မရပါ။ ဟာ့ဒ်ဝဲ မရနိုင်ပါ။" "မျက်နှာပြ လော့ခ်ဖွင့်ခြင်းကို ထပ်စမ်းကြည့်ပါ" "မျက်နှာဒေတာအသစ် သိမ်း၍မရပါ။ အဟောင်းကို အရင်ဖျက်ပါ။" @@ -682,8 +680,6 @@ "မျက်နှာမှတ်သော့ဖွင့်ခြင်း (သို့) ဖန်သားပြင်လော့ခ်ချခြင်းကို သုံးခြင်း" "ရှေ့ဆက်ရန် သင့်မျက်နှာကို သုံးပါ" "ရှေ့ဆက်ရန် သင်၏ မျက်နှာ (သို့) ဖန်သားပြင်လော့ခ်ကို သုံးပါ" - - "တစ်ခုခုမှားသွားသည်။ ထပ်စမ်းကြည့်ပါ။" "မျက်နှာသင်္ကေတ" "ထပ်တူပြုအဆင်အပြင်အားဖတ်ခြင်း" diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml index abc586770796..aaa161dc5839 100644 --- a/core/res/res/values-nb/strings.xml +++ b/core/res/res/values-nb/strings.xml @@ -663,8 +663,6 @@ "Kan ikke lage ansiktsmodell. Prøv på nytt." "Mørke briller er registrert. Ansiktet må være helt synlig." "Ansiktsdekke er registrert. Ansiktet må være helt synlig." - - "Kan ikke bekrefte ansikt. Utilgjengelig maskinvare." "Prøv ansiktslås igjen" "Kan ikke lagre nye ansiktsdata. Slett gamle data først." @@ -682,8 +680,6 @@ "Bruk ansikts- eller skjermlås" "Bruk ansiktet for å fortsette" "Bruk ansikts- eller skjermlåsen for å fortsette" - - "Noe gikk galt. Prøv på nytt." "Ansiktikon" "lese synkroniseringsinnstillinger" diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml index 60b0eeab1645..31344797169f 100644 --- a/core/res/res/values-ne/strings.xml +++ b/core/res/res/values-ne/strings.xml @@ -663,8 +663,6 @@ "तपाईंको फेस मोडेल सिर्जना गर्न सकिएन। फेरि प्रयास गर्नुहोस्।" "कालो चस्मा लगाइएको पाइयो। तपाईंको अनुहार पूरै देखिनु पर्छ।" "अनुहार छोपिएको पाइयो। तपाईंको अनुहार पूरै देखिनु पर्छ।" - - "अनुहार पुष्टि गर्न सकिएन। हार्डवेयर उपलब्ध छैन।" "फेरि फेस अनलक प्रयोग गरी हेर्नुहोस्" "अनुहारसम्बन्धी नयाँ डेटा भण्डारण गर्न सकिएन। पहिले कुनै पुरानो डेटा मेटाउनुहोस्।" @@ -682,8 +680,6 @@ "फेस अनलक वा स्क्रिन लक प्रयोग गर्नुहोस्" "जारी राख्न आफ्नो अनुहारको सहायताले पुष्टि गर्नुहोस्" "जारी राख्न आफ्नो फेस वा स्क्रिन लक प्रयोग गरी पुष्टि गर्नुहोस्" - - "केही चिज गडबड भयो। फेरि प्रयास गर्नुहोस्।" "अनुहारको आइकन" "समीकरण सेटिङहरू पढ्नुहोस्" diff --git a/core/res/res/values-night/bootleg_colors.xml b/core/res/res/values-night/bootleg_colors.xml new file mode 100644 index 000000000000..add64d966d16 --- /dev/null +++ b/core/res/res/values-night/bootleg_colors.xml @@ -0,0 +1,24 @@ + + + + + + @*android:color/system_accent3_100 + + diff --git a/core/res/res/values-night/themes_device_defaults.xml b/core/res/res/values-night/themes_device_defaults.xml index 7cfdba7a65be..cb682118f6e0 100644 --- a/core/res/res/values-night/themes_device_defaults.xml +++ b/core/res/res/values-night/themes_device_defaults.xml @@ -52,7 +52,7 @@ easier. diff --git a/core/res/res/values-night/values.xml b/core/res/res/values-night/values.xml index 1571fab66a5b..0683c20a4a4c 100644 --- a/core/res/res/values-night/values.xml +++ b/core/res/res/values-night/values.xml @@ -22,6 +22,7 @@ @color/secondary_device_default_settings @color/accent_device_default_dark @color/error_color_device_default_dark + @color/surface_header_dark_sysui ?attr/textColorPrimary @style/Theme.DeviceDefault.Dialog.Alert false diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml index 251a53a85765..eec2d4acc154 100644 --- a/core/res/res/values-nl/strings.xml +++ b/core/res/res/values-nl/strings.xml @@ -663,8 +663,6 @@ "Kan gezichtsmodel niet maken. Probeer het opnieuw." "Donkere bril waargenomen. Je gezicht moet geheel zichtbaar zijn." "Gezichtsbedekking waargenomen. Je gezicht moet geheel zichtbaar zijn." - - "Kan gezicht niet verifiëren. Hardware niet beschikbaar." "Ontgrendel opnieuw via gezichtsherkenning" "Kan nieuwe gezichten niet opslaan. Verwijder eerst een oude." @@ -682,8 +680,6 @@ "Gezicht of schermgrendeling gebruiken" "Gebruik je gezicht om door te gaan" "Gebruik je gezicht of schermvergrendeling om door te gaan" - - "Er is iets misgegaan. Probeer het opnieuw." "Gezichtspictogram" "synchronisatie-instellingen lezen" diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml index 26bfeb4acec2..8929cde9f088 100644 --- a/core/res/res/values-or/strings.xml +++ b/core/res/res/values-or/strings.xml @@ -663,8 +663,6 @@ "ଫେସର ମଡେଲ ତିଆରି କରାଯାଇପାରିବ ନାହିଁ। ପୁଣି ଚେଷ୍ଟା କର।" "କଳା ଚଷମା ଚିହ୍ନଟ କରାଯାଇଛି। ଆପଣଙ୍କ ଫେସ ସମ୍ପୂର୍ଣ୍ଣ ଭାବରେ ଦେଖାଯିବା ଆବଶ୍ଯକ।" "ଫେସରେ କଭରିଂ ଚିହ୍ନଟ କରାଯାଇଛି। ଆପଣଙ୍କ ଫେସ ସମ୍ପୂର୍ଣ୍ଣ ଭାବରେ ଦେଖାଯିବା ଆବଶ୍ଯକ।" - - "ମୁହଁ ଚିହ୍ନଟ କରିପାରିଲା ନାହିଁ। ହାର୍ଡୱେୟାର୍ ଉପଲବ୍ଧ ନାହିଁ।" "ଫେସ୍ ଅନଲକ୍ ପୁଣି ବ୍ୟବହାର କରି ଦେଖନ୍ତୁ" "ନୂଆ ମୁହଁ ଡାଟା ଷ୍ଟୋର୍ ହେବ ନାହିଁ। ପ୍ରଥମେ ପୁରୁଣାକୁ ଡିଲିଟ୍ କରନ୍ତୁ।" @@ -682,8 +680,6 @@ "ଫେସ୍ ବା ସ୍କ୍ରିନ୍ ଲକ୍ ବ୍ୟବହାର କରନ୍ତୁ" "ଜାରି ରଖିବାକୁ ଆପଣଙ୍କ ଚେହେରା ବ୍ୟବହାର କରନ୍ତୁ" "ଜାରି ରଖିବାକୁ ଆପଣଙ୍କ ଚେହେରା କିମ୍ବା ସ୍କ୍ରିନ୍ ଲକ୍ ବ୍ୟବହାର କରନ୍ତୁ" - - "କିଛି ତ୍ରୁଟି ହୋଇଛି। ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।" "ଫେସ୍ ଆଇକନ୍" "ସିଙ୍କ ସେଟିଙ୍ଗକୁ ପଢ଼ନ୍ତୁ" diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml index b61b0febd33d..62e3e5bbcc44 100644 --- a/core/res/res/values-pa/strings.xml +++ b/core/res/res/values-pa/strings.xml @@ -663,8 +663,6 @@ "ਤੁਹਾਡੇ ਚਿਹਰੇ ਦਾ ਮਾਡਲ ਨਹੀਂ ਬਣਿਆ। ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।" "ਧੁੱਪ ਦੀਆਂ ਐਨਕਾਂ ਦਾ ਪਤਾ ਲੱਗਾ। ਤੁਹਾਡਾ ਪੂਰਾ ਚਿਹਰਾ ਦਿਸਣਾ ਲਾਜ਼ਮੀ ਹੈ।" "ਚਿਹਰਾ ਢੱਕਿਆ ਹੋਣ ਦਾ ਪਤਾ ਲੱਗਾ। ਤੁਹਾਡਾ ਪੂਰਾ ਚਿਹਰਾ ਦਿਸਣਾ ਲਾਜ਼ਮੀ ਹੈ।" - - "ਚਿਹਰੇ ਦੀ ਪੁਸ਼ਟੀ ਨਹੀਂ ਹੋ ਸਕੀ। ਹਾਰਡਵੇਅਰ ਉਪਲਬਧ ਨਹੀਂ।" "ਫ਼ੇਸ ਅਣਲਾਕ ਦੁਬਾਰਾ ਵਰਤ ਕੇ ਦੇਖੋ" "ਨਵਾਂ ਚਿਹਰਾ ਡਾਟਾ ਸਟੋਰ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ। ਪਹਿਲਾਂ ਪੁਰਾਣਾ ਹਟਾਓ।" @@ -682,8 +680,6 @@ "ਫ਼ੇਸ ਜਾਂ ਸਕ੍ਰੀਨ ਲਾਕ ਦੀ ਵਰਤੋਂ ਕਰੋ" "ਜਾਰੀ ਰੱਖਣ ਲਈ ਆਪਣੇ ਚਿਹਰੇ ਦੀ ਵਰਤੋਂ ਕਰੋ" "ਜਾਰੀ ਰੱਖਣ ਲਈ ਆਪਣਾ ਚਿਹਰਾ ਜਾਂ ਸਕ੍ਰੀਨ ਲਾਕ ਵਰਤੋ" - - "ਕੋਈ ਗੜਬੜ ਹੋ ਗਈ। ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।" "ਚਿਹਰਾ ਪ੍ਰਤੀਕ" "ਸਿੰਕ ਸੈਟਿੰਗਾਂ ਪੜ੍ਹੋ" diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml index 7193eb7cb3ad..1be1d374d5ae 100644 --- a/core/res/res/values-pl/strings.xml +++ b/core/res/res/values-pl/strings.xml @@ -665,8 +665,6 @@ "Nie można utworzyć modelu twarzy. Spróbuj ponownie." "Wykryto ciemne okulary. Twarz musi być widoczna w całości." "Wykryto zasłonę twarzy. Twarz musi być widoczna w całości." - - "Nie można zweryfikować twarzy. Sprzęt niedostępny." "Spróbuj ponownie użyć rozpoznawania twarzy" "Nie można przechowywać nowych danych twarzy. Usuń stare." @@ -684,8 +682,6 @@ "Używaj rozpoznawania twarzy lub blokady ekranu" "Użyj skanu twarzy, aby kontynuować" "Aby kontynuować, użyj rozpoznawania twarzy lub blokady ekranu" - - "Coś poszło nie tak. Spróbuj ponownie." "Ikona twarzy" "czytanie ustawień synchronizacji" diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml index c1c0d5440304..8fc4c480bb7c 100644 --- a/core/res/res/values-pt-rBR/strings.xml +++ b/core/res/res/values-pt-rBR/strings.xml @@ -664,8 +664,6 @@ "Falha ao criar o modelo de rosto. Tente de novo." "Óculos escuros detectados. Seu rosto precisa estar completamente visível." "Máscara detectada. Seu rosto precisa estar completamente visível." - - "Impossível verificar rosto. Hardware indisponível." "Tente usar o Desbloqueio facial novamente" "Não é possível salvar dados faciais. Exclua dados antigos." @@ -683,8 +681,6 @@ "Usar reconhecimento facial ou bloqueio de tela" "Use seu rosto para continuar" "Use seu rosto ou o bloqueio de tela para continuar" - - "Algo deu errado. Tente de novo." "Ícone facial" "ler as configurações de sincronização" diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml index 1fe6e1c2c3af..ddb927841d1e 100644 --- a/core/res/res/values-pt-rPT/strings.xml +++ b/core/res/res/values-pt-rPT/strings.xml @@ -664,8 +664,6 @@ "Não é possível criar o seu modelo de rosto. Tente novamente." "Óculos escuros detetados. O seu rosto tem de estar completamente visível." "Cobertura facial detetada. O seu rosto tem de estar completamente visível." - - "Não pode validar o rosto. Hardware não disponível." "Experimente o Desbloqueio facial novamente" "Não pode guardar novos dados de rostos. Elimine um antigo." @@ -683,8 +681,6 @@ "Utilizar o bloqueio através do rosto ou de ecrã" "Utilize o rosto para continuar" "Utilize o rosto ou o bloqueio de ecrã para continuar" - - "Algo correu mal. Tente novamente." "Ícone de rosto" "ler definições de sincronização" diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml index c1c0d5440304..8fc4c480bb7c 100644 --- a/core/res/res/values-pt/strings.xml +++ b/core/res/res/values-pt/strings.xml @@ -664,8 +664,6 @@ "Falha ao criar o modelo de rosto. Tente de novo." "Óculos escuros detectados. Seu rosto precisa estar completamente visível." "Máscara detectada. Seu rosto precisa estar completamente visível." - - "Impossível verificar rosto. Hardware indisponível." "Tente usar o Desbloqueio facial novamente" "Não é possível salvar dados faciais. Exclua dados antigos." @@ -683,8 +681,6 @@ "Usar reconhecimento facial ou bloqueio de tela" "Use seu rosto para continuar" "Use seu rosto ou o bloqueio de tela para continuar" - - "Algo deu errado. Tente de novo." "Ícone facial" "ler as configurações de sincronização" diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml index 129f8fe68d88..2070a0ff7ef8 100644 --- a/core/res/res/values-ro/strings.xml +++ b/core/res/res/values-ro/strings.xml @@ -664,8 +664,6 @@ "Nu se poate crea modelul facial. Reîncearcă." "S-au detectat ochelari de culoare închisă. Chipul trebuie să fie vizibil în totalitate." "S-a detectat un articol care acoperă chipul. Chipul trebuie să fie vizibil în totalitate." - - "Nu se poate confirma fața. Hardware-ul nu este disponibil." "Încearcă din nou Deblocarea facială" "Nu se pot stoca date faciale noi. Șterge întâi unele vechi." @@ -683,8 +681,6 @@ "Folosește deblocarea facială sau ecranul de blocare" "Folosește-ți chipul pentru a continua" "Folosește-ți chipul sau blocarea ecranului pentru a continua" - - "A apărut o eroare. Încearcă din nou." "Pictograma chip" "să citească setări sincronizare" diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml index d01e430efd84..a99d36c20e89 100644 --- a/core/res/res/values-ru/strings.xml +++ b/core/res/res/values-ru/strings.xml @@ -665,8 +665,6 @@ "Невозможно создать модель лица. Повторите попытку." "Обнаружены темные очки. Лицо должно быть полностью видно" "Часть лица закрыта. Оно должно быть полностью видно." - - "Не удалось распознать лицо. Сканер недоступен." "Попробуйте воспользоваться фейсконтролем ещё раз." "Недостаточно места. Удалите старые данные для распознавания." @@ -684,8 +682,6 @@ "Использовать фейсконтроль или блокировку экрана" "Чтобы продолжить, используйте функцию фейсконтроля." "Чтобы продолжить, посмотрите на экран или используйте данные для разблокировки." - - "Произошла ошибка. Повторите попытку." "Значок лица" "Просмотр настроек синхронизации" diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml index ac0a3f6c7431..74b88ca8f488 100644 --- a/core/res/res/values-si/strings.xml +++ b/core/res/res/values-si/strings.xml @@ -663,8 +663,6 @@ "ඔබගේ මුහුණු ආකෘතිය තැනිය නොහැකිය. නැවත උත්සාහ කරන්න." "අඳුරු කණ්ණාඩි අනාවරණය කර ගන්නා ලදි. ඔබගේ මුහුණ සම්පූර්ණයෙන් දෘශ්‍යමාන විය යුතුය." "මුහුණු ආවරණය අනාවරණය කර ගන්නා ලදි. ඔබගේ මුහුණ සම්පූර්ණයෙන් දෘශ්‍යමාන විය යුතුය." - - "මුහුණ සත්‍යාපනය කළ නොහැක. දෘඩාංගය නොමැත." "මුහුණෙන් අගුළු හැරීම නැවත උත්සාහ කරන්න." "නව මුහුණු දත්ත ගබඩා කළ නොහැක. පළමුව පැරණි එකක් මකන්න." @@ -682,8 +680,6 @@ "මුහුණෙන් අගුළු හැරීම හෝ තිර අගුල භාවිත කරන්න" "ඉදිරියට යාමට ඔබගේ මුහුණ භාවිත කරන්න" "ඉදිරියට යාමට ඔබගේ මුහුණු හෝ තිර අගුල භාවිත කරන්න" - - "යම් දෙයක් වැරදිණි. නැවත උත්සාහ කරන්න." "මුහුණ නිරූපකය" "සමමුහුර්ත සැකසීම් කියවන්න" diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml index 5a9e0c1f032d..f0a507594c47 100644 --- a/core/res/res/values-sk/strings.xml +++ b/core/res/res/values-sk/strings.xml @@ -665,8 +665,6 @@ "Model tváre sa nedá vytvoriť. Skúste to znova." "Boli rozpoznané tmavé okuliare. Musí vám byť vidieť celú tvár." "Bolo rozpoznané rúško. Musí vám byť vidieť celú tvár." - - "Tvár sa nedá overiť. Hardvér nie je k dispozícii." "Skúste znova použiť odomknutie tvárou" "Nové údaje o tvári sa nedajú uložiť. Najprv odstráňte jeden zo starých záznamov." @@ -684,8 +682,6 @@ "Použiť tvár alebo zámku obrazovky" "Pokračujte pomocou tváre" "Pokračujte použitím tváre alebo zámky obrazovky" - - "Vyskytla sa chyba. Skúste to znova." "Ikona tváre" "čítať nastavenia synchronizácie" diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml index 56e1b5c4b580..d118c4575388 100644 --- a/core/res/res/values-sl/strings.xml +++ b/core/res/res/values-sl/strings.xml @@ -665,8 +665,6 @@ "Modela obraza ni mogoče ustvariti. Poskusite znova." "Zaznana so temna očala. Videti se mora cel obraz." "Zaznano je, da je obraz prekrit. Videti se mora cel obraz." - - "Obraza ni mogoče preveriti. Str. opr. ni na voljo." "Znova izvedite odklepanje z obrazom." "Novega obraza ni mogoče shraniti. Najprej izbrišite starega." @@ -684,8 +682,6 @@ "Uporaba odklepanja z obrazom ali s poverilnico" "Uporabite obraz, če želite nadaljevati." "Za nadaljevanje uporabite obraz ali odklepanje s poverilnico." - - "Prišlo je do napake. Poskusite znova." "Ikona obraza" "branje nastavitev sinhronizacije" diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml index bb55457aec64..1132c0c67683 100644 --- a/core/res/res/values-sq/strings.xml +++ b/core/res/res/values-sq/strings.xml @@ -663,8 +663,6 @@ "Modeli i fytyrës nuk krijohet. Provo sërish." "U zbuluan syze të errëta. Fytyra jote duhet të jetë plotësisht e dukshme." "U zbulua mbulim i fytyrës. Fytyra jote duhet të jetë plotësisht e dukshme." - - "Fytyra s\'mund të verifikohet. Hardueri nuk ofrohet." "Provo përsëri \"Shkyçjen me fytyrë\"" "S\'mund të ruhen të dhëna të reja fytyre. Fshi një të vjetër në fillim." @@ -682,8 +680,6 @@ "Përdor kyçjen me fytyrë ose kyçjen e ekranit" "Përdor fytyrën tënde për të vazhduar" "Përdor fytyrën tënde ose kyçjen e ekranit për të vazhduar" - - "Ndodhi një gabim. Provo sërish." "Ikona e fytyrës" "lexo cilësimet e sinkronizimit" diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml index 474729713186..9ae224d5ca89 100644 --- a/core/res/res/values-sr/strings.xml +++ b/core/res/res/values-sr/strings.xml @@ -664,8 +664,6 @@ "Прављење модела лица није успело. Пробајте поново." "Откривене су тамне наочари. Лице мора да буде потпуно видљиво." "Откривено је прекривање лица. Лице мора да буде потпуно видљиво." - - "Провера лица није успела. Хардвер није доступан." "Пробајте поново откључавање лицем" "Нови подаци о лицу нису сачувани. Прво избришете претходне." @@ -683,8 +681,6 @@ "Користите закључавање лицем или закључавање екрана" "Потврдите идентитет лицем да бисте наставили" "Користите лице или закључавање екрана да бисте наставили" - - "Дошло је до проблема. Пробајте поново." "Икона лица" "читање подешавања синхронизације" diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml index 92e0cd6e9d1c..db4cc268fa19 100644 --- a/core/res/res/values-sv/strings.xml +++ b/core/res/res/values-sv/strings.xml @@ -663,8 +663,6 @@ "Ansiktsmodellen kunde inte skapas. Försök igen." "Mörka glasögon identifierades. Hela ansiktet måste synas." "Något som täcker ansiktet identifierades. Hela ansiktet måste synas." - - "Ansiktsverifiering går ej. Otillgänglig maskinvara." "Försök att använda ansiktslåset igen" "Kan inte lagra ny ansiktsdata. Radera först gammal data." @@ -682,8 +680,6 @@ "Använd ansiktslåset eller skärmlåset" "Fortsätt med hjälp av ditt ansikte" "Fortsätt med hjälp av ditt ansikte eller skärmlåset" - - "Något gick fel. Försök igen." "Ansikte" "läsa synkroniseringsinställningar" diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml index fae3b07a8b7b..f4099b04719d 100644 --- a/core/res/res/values-sw/strings.xml +++ b/core/res/res/values-sw/strings.xml @@ -663,8 +663,6 @@ "Imeshindwa kuunda muundo wa uso wako. Jaribu tena." "Vioo vyeusi vimetambuliwa. Ni lazima uso wako wote uonekane." "Kifuniko cha uso kimetambuliwa. Ni lazima uso wako wote uonekane." - - "Imeshindwa kuthibitisha uso. Maunzi hayapatikani." "Jaribu Kufungua kwa Uso tena" "Imeshindwa kuhifadhi data ya uso mpya. Futa wa kale kwanza." @@ -682,8 +680,6 @@ "Tumia uso au mbinu ya kufunga skrini" "Tumia uso wako ili uendelee" "Tumia uso au mbinu yako ya kufunga skrini ili uendelee" - - "Hitilafu fulani imetokea. Jaribu tena." "Aikoni ya uso" "kusoma mipangilio ya usawazishaji" diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml index b08b86b0cb39..7f7008b57a23 100644 --- a/core/res/res/values-ta/strings.xml +++ b/core/res/res/values-ta/strings.xml @@ -663,8 +663,6 @@ "முகத் தோற்றம் பதிவாகவில்லை. மீண்டும் முயலவும்." "அடர் நிறக் கண்ணாடிகள் கண்டறியப்பட்டுள்ளது. உங்கள் முகத்தை முழுமையாகக் காட்டவும்." "முகம் மறைக்கப்பட்டுள்ளது. உங்கள் முகத்தை முழுமையாகக் காட்டவும்." - - "முகத்தைச் சரிபார்க்க இயலவில்லை. வன்பொருள் இல்லை." "மீண்டும் \'முகம் காட்டித் திறத்தலை\' உபயோகிக்கவும்" "புதிய முகங்களைச் சேர்க்க இயலவில்லை. பழையது ஒன்றை நீக்கவும்." @@ -682,8 +680,6 @@ "முகம் காட்டித் திறத்தல் / திரைப் பூட்டைப் பயன்படுத்து" "தொடர்வதற்கு உங்கள் முகத்தைப் பயன்படுத்துங்கள்" "தொடர, உங்கள் முகத்தையோ திரைப் பூட்டையோ பயன்படுத்துங்கள்" - - "ஏதோ தவறாகிவிட்டது. மீண்டும் முயலவும்." "முக ஐகான்" "ஒத்திசைவு அமைப்புகளைப் படித்தல்" diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml index c946894bcd54..3c706ff321c7 100644 --- a/core/res/res/values-te/strings.xml +++ b/core/res/res/values-te/strings.xml @@ -663,8 +663,6 @@ "మీ ఫేస్‌మోడల్ క్రియేషన్ కుదరదు. మళ్లీ ట్రై చేయండి." "డార్క్ గ్లాసెస్ గుర్తించబడ్డాయి. మీ ముఖం పూర్తిగా కనిపించాలి." "ముఖం కవర్ చేయబడింది. మీ ముఖం పూర్తిగా కనిపించాలి." - - "ముఖం ధృవీకరించలేరు. హార్డ్‌వేర్ అందుబాటులో లేదు." "ఫేస్ అన్‌లాక్‌ను మళ్లీ ట్రై చేయండి" "కొత్త ముఖం డేటాను నిల్వ చేయడం కాదు. మొదట పాతది తొలిగించండి." @@ -682,8 +680,6 @@ "ఫేస్ లేదా స్క్రీన్ లాక్‌ను ఉపయోగించండి" "కొనసాగించడానికి మీ ముఖాన్ని ఉపయోగించండి" "కొనసాగించడానికి మీ ముఖం లేదా స్క్రీన్ లాక్‌ను ఉపయోగించండి" - - "ఏదో తప్పు జరిగింది. మళ్లీ ట్రై చేయండి." "ముఖ చిహ్నం" "సింక్ సెట్టింగ్‌లను చదవగలగడం" diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml index 6af51659b336..95eabdabff31 100644 --- a/core/res/res/values-th/strings.xml +++ b/core/res/res/values-th/strings.xml @@ -663,8 +663,6 @@ "สร้างรูปแบบใบหน้าไม่ได้ โปรดลองอีกครั้ง" "ตรวจพบแว่นตาดำ ต้องมองเห็นใบหน้าของคุณทั้งหมด" "ตรวจพบหน้ากากอนามัย ต้องมองเห็นใบหน้าของคุณทั้งหมด" - - "ยืนยันใบหน้าไม่ได้ ฮาร์ดแวร์ไม่พร้อมใช้งาน" "ลองใช้การปลดล็อกด้วยใบหน้าอีกครั้ง" "จัดเก็บข้อมูลใบหน้าใหม่ไม่ได้ ลบข้อมูลเก่าออกไปก่อน" @@ -682,8 +680,6 @@ "ใช้การล็อกด้วยใบหน้าหรือการล็อกหน้าจอ" "ใช้ใบหน้าของคุณเพื่อดำเนินการต่อ" "ใช้ใบหน้าหรือการล็อกหน้าจอเพื่อดำเนินการต่อ" - - "เกิดข้อผิดพลาด ลองอีกครั้ง" "ไอคอนใบหน้า" "อ่านการตั้งค่าการซิงค์" diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml index 7bb0ba9c91da..1e8880bf779f 100644 --- a/core/res/res/values-tl/strings.xml +++ b/core/res/res/values-tl/strings.xml @@ -663,8 +663,6 @@ "Hindi magawa ang iyong face model. Subukan ulit." "May na-detect na madilim na salamin. Dapat ganap na nakikita ang iyong mukha." "May na-detect na pantakip sa mukha. Dapat ganap na nakikita ang iyong mukha." - - "Di ma-verify ang mukha. Di available ang hardware." "Subukan ulit ang Pag-unlock Gamit ang Mukha" "Hindi ma-store ang data ng mukha. Mag-delete muna ng iba." @@ -682,8 +680,6 @@ "Gumamit ng mukha o lock ng screen" "Gamitin ang iyong mukha para magpatuloy" "Gamitin ang iyong mukha o lock ng screen para magpatuloy" - - "Nagkaproblema. Subukan ulit." "Face icon" "basahin ang mga setting ng sync" diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml index c9c2d113121f..65755f7256e3 100644 --- a/core/res/res/values-tr/strings.xml +++ b/core/res/res/values-tr/strings.xml @@ -663,8 +663,6 @@ "Yüzünüzün modeli oluşturulamıyor. Tekrar deneyin." "Koyu renk gözlükler algılandı. Yüzünüz tamamen görünür olmalıdır." "Yüzünüzü kapattığınız algılandı. Yüzünüz tamamen görünür olmalıdır." - - "Yüz doğrulanamıyor. Donanım kullanılamıyor." "Yüz Tanıma Kilidi\'ni yeniden deneyin" "Yeni yüz verisi depolanamıyor. Önce eski bir tanesini silin." @@ -682,8 +680,6 @@ "Yüz tanıma veya ekran kilidi kullan" "Devam etmek için yüzünüzü kullanın" "Devam etmek için yüz veya ekran kilidinizi kullanın" - - "Bir hata oluştu. Tekrar deneyin." "Yüz simgesi" "senk. ayarlarını okuma" diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml index 163c629dbaa9..7056972d8478 100644 --- a/core/res/res/values-uk/strings.xml +++ b/core/res/res/values-uk/strings.xml @@ -665,8 +665,6 @@ "Не вдається створити модель обличчя. Повторіть спробу." "Виявлено темні окуляри. Обличчя має бути видно повністю." "Виявлено аксесуар, який закриває обличчя. Обличчя має бути видно повністю." - - "Не вдається перевірити обличчя. Апаратне забезпечення недоступне." "Скористайтеся фейсконтролем ще раз" "Не вдається зберегти нові дані про обличчя. Видаліть старі." @@ -684,8 +682,6 @@ "Використовувати фейсконтроль або дані для розблокування екрана" "Щоб продовжити, скористайтеся фейсконтролем" "Щоб продовжити, скористайтеся фейсконтролем або даними для розблокування екрана" - - "Сталася помилка. Повторіть спробу." "Значок обличчя" "читати налаштування синхронізації" diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml index 94f5ab525cd4..17aa7072ca6a 100644 --- a/core/res/res/values-ur/strings.xml +++ b/core/res/res/values-ur/strings.xml @@ -663,8 +663,6 @@ "آپکے چہرے کا ماڈل تخلیق نہیں ہو سکا۔ پھر کوشش کریں۔" "گہرے چشمے کا پتہ چلا۔ آپ کا چہرہ مکمل طور پر دکھائی دینا ضروری ہے۔" "چہرے کو ڈھانپنے کا پتہ چلا۔ آپ کا چہرہ مکمل طور پر دکھائی دینا ضروری ہے۔" - - "چہرے کی توثیق نہیں کی جا سکی۔ ہارڈ ویئر دستیاب نہیں ہے۔" "فیس اَنلاک کو دوبارہ آزمائیں" "چہرے کا نیا ڈیٹا اسٹور نہیں کر سکتے۔ پہلے پرانا حذف کریں۔" @@ -682,8 +680,6 @@ "فیس یا اسکرین لاک استعمال کریں" "جاری رکھنے کے لیے اپنے چہرے کا استعمال کریں" "جاری رکھنے کے لیے اپنے چہرے یا اسکرین لاک کا استعمال کریں" - - "کچھ غلط ہو گیا۔ دوبارہ کوشش کریں۔" "چہرے کا آئیکن" "مطابقت پذیری کی ترتیبات پڑھیں" diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml index 92b231526354..4ec719bc7838 100644 --- a/core/res/res/values-uz/strings.xml +++ b/core/res/res/values-uz/strings.xml @@ -663,8 +663,6 @@ "Yuzingiz modeli yaratilmadi. Qayta urining." "Qora koʻzoynak aniqlandi. Yuzingiz toʻliq koʻrinishi kerak." "Yuzning bir qismi yopilib qolgan. Yuzingiz toʻliq koʻrinishi kerak." - - "Yuzingiz tasdiqlanmadi. Qurilma ishlamayapti." "Yana yuz bilan ochishga urining" "Yuzga oid axborot saqlanmadi. Avval eskilari tozalansin." @@ -682,8 +680,6 @@ "Yuz bilan ochish yoki ekran qulfi" "Yuz tekshiruvi bilan davom eting" "Davom etish uchun yuz tekshiruvi yoki ekran qulfidan foydalaning" - - "Xatolik yuz berdi. Qayta urining." "Yuz belgisi" "sinx-sh sozlamalarini o‘qish" diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml index f78c64e6a957..fb009ef4e607 100644 --- a/core/res/res/values-vi/strings.xml +++ b/core/res/res/values-vi/strings.xml @@ -663,8 +663,6 @@ "Không thể tạo mẫu khuôn mặt của bạn. Hãy thử lại." "Đã phát hiện thấy kính râm. Toàn bộ khuôn mặt của bạn phải được trông thấy rõ ràng." "Đã phát hiện khuôn mặt bị che khuất. Toàn bộ khuôn mặt của bạn phải được hiển thị." - - "Không thể xác minh khuôn mặt. Phần cứng không có sẵn." "Hãy thử lại thao tác Mở khóa bằng khuôn mặt" "Không lưu được dữ liệu khuôn mặt mới. Hãy xóa dữ liệu cũ trước." @@ -682,8 +680,6 @@ "Dùng khuôn mặt hoặc phương thức khóa màn hình" "Hãy dùng khuôn mặt của bạn để tiếp tục" "Dùng khuôn mặt của bạn hoặc phương thức khóa màn hình để tiếp tục" - - "Đã xảy ra lỗi. Hãy thử lại." "Biểu tượng khuôn mặt" "đọc cài đặt đồng bộ hóa" diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml index 023d9d1f67d2..d5202f57fc71 100644 --- a/core/res/res/values-zh-rCN/strings.xml +++ b/core/res/res/values-zh-rCN/strings.xml @@ -663,8 +663,6 @@ "无法创建您的脸部模型,请重试。" "检测到墨镜,您的脸部必须完全可见。" "检测到脸部有遮挡物,您的脸部必须完全可见。" - - "无法验证人脸。硬件无法使用。" "请重新尝试人脸解锁" "无法存储新的人脸数据。请先删除旧的人脸数据。" @@ -682,8 +680,6 @@ "使用人脸解锁或屏幕锁定凭据" "使用您的面孔验证身份才能继续" "使用人脸解锁或屏幕锁定凭据验证身份,才能继续操作" - - "出了点问题。请重试。" "面孔图标" "读取同步设置" diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml index c67f9fa598ea..27de26cbabec 100644 --- a/core/res/res/values-zh-rHK/strings.xml +++ b/core/res/res/values-zh-rHK/strings.xml @@ -663,8 +663,6 @@ "無法建立面部模型,請再試一次。" "偵測到深色眼鏡。您必須展示整個面孔。" "偵測到面部遮蓋物。您必須展示整個面孔。" - - "無法驗證面孔,硬件無法使用。" "請再次嘗試「面孔解鎖」" "無法儲存新的臉容資料,請先刪除舊資料。" @@ -682,8 +680,6 @@ "使用面孔或螢幕鎖定" "如要繼續操作,請使用您的面孔驗證身分" "請使用面孔解鎖或螢幕鎖定功能驗證身分,才能繼續操作" - - "發生錯誤,請再試一次。" "面孔圖示" "讀取同步處理設定" diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml index 3da603d33e1e..723bf3abc994 100644 --- a/core/res/res/values-zh-rTW/strings.xml +++ b/core/res/res/values-zh-rTW/strings.xml @@ -663,8 +663,6 @@ "無法建立臉部模型,請再試一次。" "偵測到墨鏡,請露出整張臉。" "偵測到有物品遮住臉,請露出整張臉。" - - "相關硬體無法使用,因此無法驗證臉孔。" "請重新進行人臉解鎖" "無法儲存新的臉孔資料,請先刪除舊的資料。" @@ -682,8 +680,6 @@ "使用人臉解鎖或螢幕鎖定功能" "如要繼續操作,請透過你的臉孔驗證身分" "請使用人臉解鎖或螢幕鎖定功能驗證身分,才能繼續操作" - - "發生錯誤,請再試一次。" "臉孔圖示" "讀取同步處理設定" diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml index 9028854b506a..9eb395ede883 100644 --- a/core/res/res/values-zu/strings.xml +++ b/core/res/res/values-zu/strings.xml @@ -663,8 +663,6 @@ "Ayikwazi ukusungula imodeli yobuso bakho. Zama futhi." "Kutholwe izibuko ezimnyama. Ubuso bakho kufanele bubonakale ngokugcwele." "Kutholwe ukumbozwa kobuso. Ubuso bakho kufanele bubonakale ngokugcwele." - - "Ayikwazi ukuqinisekisa ubuso. Izingxenyekazi zekhompyutha azitholakali." "Zama Ukuvula ngobuso futhi." "Ayikwazi ukulondoloza idatha yobuso. Susa endala." @@ -682,8 +680,6 @@ "Sebenzisa i-face lock noma ukukhiya isikrini" "Sebenzisa ubuso bakho ukuze uqhubeke" "Sebenzisa ubuso bakho noma ukukhiya isikrini ukuze uqhubeke" - - "Kunento engahambanga kahle. Zama futhi." "Isithonjana sobuso" "funda izilungiselelo zokuvumelanisa" diff --git a/core/res/res/values/bootleg_arrays.xml b/core/res/res/values/bootleg_arrays.xml new file mode 100644 index 000000000000..ee003774d75c --- /dev/null +++ b/core/res/res/values/bootleg_arrays.xml @@ -0,0 +1,9 @@ + + + + restart + reboot_recovery + reboot_bootloader + + + diff --git a/core/res/res/values/bootleg_colors.xml b/core/res/res/values/bootleg_colors.xml new file mode 100644 index 000000000000..aede057fa5e9 --- /dev/null +++ b/core/res/res/values/bootleg_colors.xml @@ -0,0 +1,38 @@ + + + + + + @*android:color/system_accent3_600 + + + @*android:color/system_accent1_100 + @*android:color/system_accent1_900 + + + #025fee + #ee9002 + #02d6ee + #9002ee + #ee0260 + #00b200 + #7200b6 + #006f72 + #eed315 + diff --git a/core/res/res/values/bootleg_config.xml b/core/res/res/values/bootleg_config.xml new file mode 100644 index 000000000000..e0d935402820 --- /dev/null +++ b/core/res/res/values/bootleg_config.xml @@ -0,0 +1,147 @@ + + + + + + true + + + false + + + sans-serif-light + + + sans-serif-regular + + + + + + + false + + + 0 + + + + + + + + + + + + + + + + + + + + + + + false + + + false + + + false + + + false + + + + @string/face_unlock_disabled_idle + + + + false + + + false + + + false + + + 9 + + + + + + + + + + + + com.android.contacts + com.android.chrome + com.android.documentsui + com.android.gallery3d + com.android.vending + com.google.android.apps.docs + com.google.android.apps.messaging + com.google.android.apps.nbu.files + com.google.android.apps.photos + com.google.android.apps.recorder + com.google.android.contacts + com.google.android.dialer + com.google.android.gm + com.google.android.googlequicksearchbox + com.google.android.keep + com.google.android.youtube + org.lineageos.recorder + org.lineageos.aperture + + + + true + + + false + 250 + false + + + + + + false + false + + + + + + false + diff --git a/core/res/res/values/bootleg_dimens.xml b/core/res/res/values/bootleg_dimens.xml new file mode 100644 index 000000000000..3b19bd7a9ce5 --- /dev/null +++ b/core/res/res/values/bootleg_dimens.xml @@ -0,0 +1,18 @@ + + + + + 350dp + 175dp + 56dp + 24sp + 56dp + 16sp + 56dp + 24dp + 14sp + 8dp + diff --git a/core/res/res/values/bootleg_strings.xml b/core/res/res/values/bootleg_strings.xml new file mode 100644 index 000000000000..a1647946e45a --- /dev/null +++ b/core/res/res/values/bootleg_strings.xml @@ -0,0 +1,48 @@ + + + + + + Face unlock disabled due to inactivity + + + Reboot bootloader + Rebooting to bootloader\u2026 + Reboot recovery + Rebooting to recovery\u2026 + Restart system + Restarting system\u2026 + + + %1$s + + + Unlock %1$s + + + Press and hold power button to unlock + Pocket mode is on + To use your phone: + Check if the blue area of your phone is clear of any obstructions, and clean off any dirt or dust. + Long press the power button to force quit pocket mode. + 1.\u0020 + 2.\u0020 + + + diff --git a/core/res/res/values/bootleg_styles.xml b/core/res/res/values/bootleg_styles.xml new file mode 100644 index 000000000000..4209e6cc8cde --- /dev/null +++ b/core/res/res/values/bootleg_styles.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + diff --git a/core/res/res/values/bootleg_symbols.xml b/core/res/res/values/bootleg_symbols.xml new file mode 100644 index 000000000000..57551a973bd8 --- /dev/null +++ b/core/res/res/values/bootleg_symbols.xml @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/core/res/res/values/colors_device_defaults.xml b/core/res/res/values/colors_device_defaults.xml index 4c5a008a3402..5d3d6a3155a9 100644 --- a/core/res/res/values/colors_device_defaults.xml +++ b/core/res/res/values/colors_device_defaults.xml @@ -59,7 +59,7 @@ @color/system_neutral1_700 @color/system_neutral2_100 @color/system_neutral1_800 - @color/system_neutral1_0 + @color/system_neutral1_10 @color/text_color_primary_device_default_light diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 5d1acbc5a07a..6b16a528fbb9 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -58,10 +58,13 @@ @string/status_bar_ethernet @string/status_bar_wifi @string/status_bar_hotspot + @string/status_bar_ims @string/status_bar_mobile @string/status_bar_airplane @string/status_bar_battery @string/status_bar_sensors_off + @string/status_bar_center_clock + @string/status_bar_right_clock rotate @@ -100,6 +103,9 @@ call_strength sensors_off screen_record + center_clock + right_clock + ims - 200 + 100 - 400 + 200 - 500 + 250 - 150 - 220 + 75 + 110 116 @@ -3216,6 +3222,7 @@ lockdown power restart + recovery logout screenshot bugreport @@ -3797,7 +3804,7 @@ - + com.android.statementservice,com.android.systemui @@ -4350,7 +4357,7 @@ 90 - + @string/config_bodyFontFamily @@ -4428,7 +4435,7 @@ true - @string/font_family_button_material + @string/config_bodyFontFamilyMedium sans-serif diff --git a/core/res/res/values/donottranslate_material.xml b/core/res/res/values/donottranslate_material.xml index 9cf9f6cfa86d..013b9a8ecb9e 100644 --- a/core/res/res/values/donottranslate_material.xml +++ b/core/res/res/values/donottranslate_material.xml @@ -17,16 +17,16 @@ sans-serif-light - sans-serif - sans-serif - sans-serif - sans-serif - sans-serif-medium - sans-serif - sans-serif - sans-serif-medium - sans-serif - sans-serif - sans-serif-medium + @string/config_bodyFontFamily + @string/config_bodyFontFamily + @string/config_bodyFontFamily + @string/config_headlineFontFamily + @string/config_headlineFontFamilyMedium + @string/config_bodyFontFamily + @string/config_bodyFontFamily + @string/config_bodyFontFamilyMedium + @string/config_bodyFontFamily + @string/config_bodyFontFamily + @string/config_bodyFontFamilyMedium diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 5763345aba4d..9e82d0dbb1b5 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -974,6 +974,18 @@ + + Spoof package signature + + Allows the app to pretend to be a different app. Malicious applications might be able to use this to access private application data. Legitimate uses include an emulator pretending to be what it emulates. Grant this permission with caution only! + + Spoof package signature + + allow to spoof package signature + + Allow + <b>%1$s</b> to spoof package signature? + disable or modify status bar @@ -1894,9 +1906,6 @@ Use your face or screen lock to continue - - - Something went wrong. Try again. @@ -5190,16 +5199,16 @@ -- - sans-serif + @string/config_bodyFontFamily - sans-serif + @string/config_bodyFontFamily - sans-serif-medium + @string/config_bodyFontFamilyMedium - sans-serif-medium + @string/config_bodyFontFamilyMedium - sans-serif-medium + @string/config_bodyFontFamilyMedium Ask for PIN before unpinning diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml index bad05e077b7d..884581cf4a8e 100644 --- a/core/res/res/values/styles.xml +++ b/core/res/res/values/styles.xml @@ -974,7 +974,7 @@ please see styles_device_defaults.xml. diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml index eec6ae3fb521..2446054d8967 100644 --- a/core/res/res/values/styles_material.xml +++ b/core/res/res/values/styles_material.xml @@ -429,7 +429,7 @@ please see styles_device_defaults.xml. diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 6e574bd788a9..eb1f6e6ac7c8 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -3134,6 +3134,7 @@ + @@ -4854,4 +4855,7 @@ + + + diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml index c603c83e033d..721812b76d9a 100644 --- a/core/res/res/values/themes_device_defaults.xml +++ b/core/res/res/values/themes_device_defaults.xml @@ -1089,6 +1089,7 @@ easier. @style/TextAppearance.DeviceDefault.Widget.Button + @style/TextAppearance.DeviceDefault.Medium @dimen/config_buttonCornerRadius @@ -2154,6 +2155,7 @@ easier. @style/TextAppearance.DeviceDefault.Widget.Button + @style/TextAppearance.DeviceDefault.Medium @dimen/config_buttonCornerRadius @@ -2299,8 +2301,8 @@ easier. @color/edge_effect_device_default_light - @color/navigation_bar_divider_device_default_settings - @android:color/white + @null + ?attr/colorBackground true diff --git a/core/tests/benchmarks/src/android/os/ParcelableBenchmark.java b/core/tests/benchmarks/src/android/os/ParcelableBenchmark.java index 1cf430205627..372bca4664a2 100644 --- a/core/tests/benchmarks/src/android/os/ParcelableBenchmark.java +++ b/core/tests/benchmarks/src/android/os/ParcelableBenchmark.java @@ -88,6 +88,7 @@ public void timeReadWritePointArrayFast(int reps) { } } + @SuppressWarnings("ParcelableCreator") @SuppressLint("ParcelCreator") private static class PointArray implements Parcelable { Rect mBounds = new Rect(); diff --git a/core/tests/coretests/src/android/content/pm/PackageManagerPropertyTests.java b/core/tests/coretests/src/android/content/pm/PackageManagerPropertyTests.java index d505492f3b80..86e958320a04 100644 --- a/core/tests/coretests/src/android/content/pm/PackageManagerPropertyTests.java +++ b/core/tests/coretests/src/android/content/pm/PackageManagerPropertyTests.java @@ -20,6 +20,7 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertThrows; import static org.junit.Assert.fail; import android.content.pm.PackageManager.Property; @@ -162,40 +163,30 @@ public void testStringPropertyToBundle() throws Exception { @Test public void testProperty_invalidName() throws Exception { - try { + assertThrows(NullPointerException.class, () -> { final Property p = new Property(null, 1, "android", null); - fail("expected assertion error"); - } catch (AssertionError expected) { - } + }); } @Test public void testProperty_invalidType() throws Exception { - try { + assertThrows(IllegalArgumentException.class, () -> { final Property p = new Property("invalidTypeProperty", 0, "android", null); - fail("expected assertion error"); - } catch (AssertionError expected) { - } + }); - try { + assertThrows(IllegalArgumentException.class, () -> { final Property p = new Property("invalidTypeProperty", 6, "android", null); - fail("expected assertion error"); - } catch (AssertionError expected) { - } + }); - try { + assertThrows(IllegalArgumentException.class, () -> { final Property p = new Property("invalidTypeProperty", -1, "android", null); - fail("expected assertion error"); - } catch (AssertionError expected) { - } + }); } @Test public void testProperty_noPackageName() throws Exception { - try { + assertThrows(NullPointerException.class, () -> { final Property p = new Property(null, 1, null, null); - fail("expected assertion error"); - } catch (AssertionError expected) { - } + }); } } diff --git a/core/tests/coretests/src/android/widget/RemoteViewsTest.java b/core/tests/coretests/src/android/widget/RemoteViewsTest.java index 00b3693c902b..bbf9f3c99402 100644 --- a/core/tests/coretests/src/android/widget/RemoteViewsTest.java +++ b/core/tests/coretests/src/android/widget/RemoteViewsTest.java @@ -128,6 +128,7 @@ public void clone_child_fails() { RemoteViews clone = child.clone(); } + @SuppressWarnings("ReturnValueIgnored") @Test public void clone_repeatedly() { RemoteViews original = new RemoteViews(mPackage, R.layout.remote_views_test); @@ -485,6 +486,7 @@ public View waitAndGetView() throws Exception { } } + @SuppressWarnings("ReturnValueIgnored") @Test public void nestedAddViews() { RemoteViews views = new RemoteViews(mPackage, R.layout.remote_views_test); @@ -509,6 +511,7 @@ public void nestedAddViews() { parcelAndRecreate(views); } + @SuppressWarnings("ReturnValueIgnored") @Test public void nestedLandscapeViews() { RemoteViews views = new RemoteViews(mPackage, R.layout.remote_views_test); diff --git a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java index 82b2bf4185e6..8207c9ee5ff3 100644 --- a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java @@ -1054,10 +1054,23 @@ class TestBinderCallsStats extends BinderCallsStats { super(new Injector() { public Random getRandomGenerator() { return new Random() { - int mCallCount = 0; + int mCallCount = -1; public int nextInt() { - return mCallCount++; + throw new IllegalStateException("Should not use nextInt()"); + } + + public int nextInt(int x) { + if (mCallCount == -1) { + // The tests are written such that they expect + // the first call to nextInt() to be on the first + // callEnded(). However, the BinderCallsStats + // constructor also calls nextInt(). Fake 0 being + // rolled twice. + mCallCount++; + return 0; + } + return (mCallCount++) % x; } }; } diff --git a/core/tests/coretests/src/com/android/internal/os/BinderLatencyObserverTest.java b/core/tests/coretests/src/com/android/internal/os/BinderLatencyObserverTest.java index 5af7376dc132..7bd53b9d4fcc 100644 --- a/core/tests/coretests/src/com/android/internal/os/BinderLatencyObserverTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BinderLatencyObserverTest.java @@ -98,7 +98,7 @@ public void testSampling() { assertEquals(1, latencyHistograms.size()); LatencyDims dims = latencyHistograms.keySet().iterator().next(); assertEquals(binder.getClass(), dims.getBinderClass()); - assertEquals(1, dims.getTransactionCode()); + assertEquals(2, dims.getTransactionCode()); // the first nextInt() is in the constructor assertThat(latencyHistograms.get(dims)).asList().containsExactly(1, 0, 0, 0, 0).inOrder(); } @@ -313,11 +313,11 @@ public Random getRandomGenerator() { int mCallCount = 0; public int nextInt() { - return mCallCount++; + throw new IllegalStateException("Should not use nextInt()"); } public int nextInt(int x) { - return 1; + return (mCallCount++) % x; } }; } diff --git a/core/tests/coretests/src/com/android/internal/util/TokenBucketTest.java b/core/tests/coretests/src/com/android/internal/util/TokenBucketTest.java index 6c50bce86638..8b30828a8936 100644 --- a/core/tests/coretests/src/com/android/internal/util/TokenBucketTest.java +++ b/core/tests/coretests/src/com/android/internal/util/TokenBucketTest.java @@ -16,6 +16,8 @@ package com.android.internal.util; +import static org.junit.Assert.assertThrows; + import android.os.SystemClock; import android.text.format.DateUtils; @@ -170,10 +172,9 @@ void assertDuration(long expected, long elapsed) { } void assertThrow(Fn fn) { - try { + assertThrows(Throwable.class, () -> { fn.call(); - fail("expected n exception to be thrown."); - } catch (Throwable t) { } + }); } interface Fn { void call(); } diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/src/com/android/multidexlegacytestapp/Test.java b/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/src/com/android/multidexlegacytestapp/Test.java index 41b8956f55d0..a2263256508b 100644 --- a/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/src/com/android/multidexlegacytestapp/Test.java +++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/src/com/android/multidexlegacytestapp/Test.java @@ -31,6 +31,7 @@ public void testAllClassesAvailable() { assertEquals(3366, getActivity().getValue()); } + @SuppressWarnings("ReturnValueIgnored") public void testAnnotation() throws Exception { assertEquals(ReferencedByAnnotation.B, ((AnnotationWithEnum) TestApplication.annotation).value()); diff --git a/core/tests/utiltests/src/com/android/internal/util/CallbackRegistryTest.java b/core/tests/utiltests/src/com/android/internal/util/CallbackRegistryTest.java index c53f4cc7ee52..1581abb5a9c6 100644 --- a/core/tests/utiltests/src/com/android/internal/util/CallbackRegistryTest.java +++ b/core/tests/utiltests/src/com/android/internal/util/CallbackRegistryTest.java @@ -20,6 +20,7 @@ import org.junit.Test; import java.util.ArrayList; +import java.util.Objects; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -39,11 +40,11 @@ public class CallbackRegistryTest extends TestCase { Integer argValue; private void addNotifyCount(Integer callback) { - if (callback == callback1) { + if (Objects.equals(callback, callback1)) { notify1++; - } else if (callback == callback2) { + } else if (Objects.equals(callback, callback2)) { notify2++; - } else if (callback == callback3) { + } else if (Objects.equals(callback, callback3)) { notify3++; } deepNotifyCount[callback]++; @@ -114,7 +115,7 @@ public void testRemoveWhileNotifying() { public void onNotifyCallback(Integer callback, CallbackRegistryTest sender, int arg1, Integer arg) { addNotifyCount(callback); - if (callback == callback1) { + if (Objects.equals(callback, callback1)) { registry.remove(callback1); registry.remove(callback2); } @@ -166,9 +167,9 @@ public void testAddRemovedListener() { public void onNotifyCallback(Integer callback, CallbackRegistryTest sender, int arg1, Integer arg) { addNotifyCount(callback); - if (callback == callback1) { + if (Objects.equals(callback, callback1)) { registry.remove(callback2); - } else if (callback == callback3) { + } else if (Objects.equals(callback, callback3)) { registry.add(callback2); } } diff --git a/data/etc/com.android.launcher3.xml b/data/etc/com.android.launcher3.xml index 36a51341f52d..25245b87ecb6 100644 --- a/data/etc/com.android.launcher3.xml +++ b/data/etc/com.android.launcher3.xml @@ -25,5 +25,9 @@ + + + + diff --git a/data/etc/com.android.settings.xml b/data/etc/com.android.settings.xml index 5dcc5998850b..1f1c4987bd7d 100644 --- a/data/etc/com.android.settings.xml +++ b/data/etc/com.android.settings.xml @@ -27,6 +27,7 @@ + @@ -46,6 +47,7 @@ + @@ -62,5 +64,7 @@ + + diff --git a/data/etc/com.android.systemui.xml b/data/etc/com.android.systemui.xml index e0e13f59b706..1592f2001429 100644 --- a/data/etc/com.android.systemui.xml +++ b/data/etc/com.android.systemui.xml @@ -28,6 +28,7 @@ + @@ -82,5 +83,7 @@ + + diff --git a/data/etc/platform.xml b/data/etc/platform.xml index 9a1b8a90dbfd..e632a671f4a3 100644 --- a/data/etc/platform.xml +++ b/data/etc/platform.xml @@ -212,6 +212,9 @@ + + + @@ -334,4 +337,6 @@ + + diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index 58a2073981bf..c71084a0dfcf 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -42,6 +42,7 @@ applications that come with the platform + diff --git a/data/keyboards/Generic.kcm b/data/keyboards/Generic.kcm index fe6eeeb66e40..3c7178022261 100644 --- a/data/keyboards/Generic.kcm +++ b/data/keyboards/Generic.kcm @@ -498,7 +498,7 @@ key PLUS { ### Non-printing keys ### key ESCAPE { - base: none + base: fallback BACK alt, meta: fallback HOME ctrl: fallback MENU } diff --git a/data/videos/AndroidInSpace.240p.mp4 b/data/videos/AndroidInSpace.240p.mp4 deleted file mode 100644 index e1445e493c76..000000000000 Binary files a/data/videos/AndroidInSpace.240p.mp4 and /dev/null differ diff --git a/data/videos/AndroidInSpace.480p.lq.mp4 b/data/videos/AndroidInSpace.480p.lq.mp4 deleted file mode 100644 index f1db6942ebd2..000000000000 Binary files a/data/videos/AndroidInSpace.480p.lq.mp4 and /dev/null differ diff --git a/data/videos/AndroidInSpace.480p.mq.mp4 b/data/videos/AndroidInSpace.480p.mq.mp4 deleted file mode 100644 index 5f4cfb3c33b7..000000000000 Binary files a/data/videos/AndroidInSpace.480p.mq.mp4 and /dev/null differ diff --git a/data/videos/Disco.240p.mp4 b/data/videos/Disco.240p.mp4 deleted file mode 100644 index c9078b736f90..000000000000 Binary files a/data/videos/Disco.240p.mp4 and /dev/null differ diff --git a/data/videos/Disco.480p.lq.mp4 b/data/videos/Disco.480p.lq.mp4 deleted file mode 100644 index 411e0d5dacd5..000000000000 Binary files a/data/videos/Disco.480p.lq.mp4 and /dev/null differ diff --git a/data/videos/Disco.480p.mq.mp4 b/data/videos/Disco.480p.mq.mp4 deleted file mode 100644 index 3dc495764b82..000000000000 Binary files a/data/videos/Disco.480p.mq.mp4 and /dev/null differ diff --git a/data/videos/Sunset.240p.mp4 b/data/videos/Sunset.240p.mp4 deleted file mode 100644 index f5c533f0f9f5..000000000000 Binary files a/data/videos/Sunset.240p.mp4 and /dev/null differ diff --git a/data/videos/Sunset.480p.lq.mp4 b/data/videos/Sunset.480p.lq.mp4 deleted file mode 100644 index 7f9d6a1e6888..000000000000 Binary files a/data/videos/Sunset.480p.lq.mp4 and /dev/null differ diff --git a/data/videos/Sunset.480p.mq.mp4 b/data/videos/Sunset.480p.mq.mp4 deleted file mode 100644 index 7691544f019e..000000000000 Binary files a/data/videos/Sunset.480p.mq.mp4 and /dev/null differ diff --git a/data/videos/VideoPackage1.mk b/data/videos/VideoPackage1.mk deleted file mode 100644 index ee3b5ed321dc..000000000000 --- a/data/videos/VideoPackage1.mk +++ /dev/null @@ -1,26 +0,0 @@ -# -# Copyright (C) 2011 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -# Lower-quality videos for space-constrained devices - -LOCAL_PATH := frameworks/base/data/videos -TARGET_PATH := system/media/video - -#PRODUCT_COPY_FILES += \ -# $(LOCAL_PATH)/AndroidInSpace.240p.mp4:$(TARGET_PATH)/AndroidInSpace.240p.mp4 \ -# $(LOCAL_PATH)/AndroidInSpace.480p.lq.mp4:$(TARGET_PATH)/AndroidInSpace.480p.mp4 \ -# $(LOCAL_PATH)/Sunset.240p.mp4:$(TARGET_PATH)/Sunset.240p.mp4 \ -# $(LOCAL_PATH)/Sunset.480p.lq.mp4:$(TARGET_PATH)/Sunset.480p.mp4 diff --git a/data/videos/VideoPackage2.mk b/data/videos/VideoPackage2.mk deleted file mode 100644 index f799bea2c147..000000000000 --- a/data/videos/VideoPackage2.mk +++ /dev/null @@ -1,26 +0,0 @@ -# -# Copyright (C) 2011 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -# Good-quality videos for non-space-constrained devices - -LOCAL_PATH := frameworks/base/data/videos -TARGET_PATH := system/media/video - -#PRODUCT_COPY_FILES += \ -# $(LOCAL_PATH)/AndroidInSpace.240p.mp4:$(TARGET_PATH)/AndroidInSpace.240p.mp4 \ -# $(LOCAL_PATH)/AndroidInSpace.480p.mq.mp4:$(TARGET_PATH)/AndroidInSpace.480p.mp4 \ -# $(LOCAL_PATH)/Sunset.240p.mp4:$(TARGET_PATH)/Sunset.240p.mp4 \ -# $(LOCAL_PATH)/Sunset.480p.mq.mp4:$(TARGET_PATH)/Sunset.480p.mp4 diff --git a/errorprone/java/com/google/errorprone/bugpatterns/android/EfficientXmlChecker.java b/errorprone/java/com/google/errorprone/bugpatterns/android/EfficientXmlChecker.java index 8706a68226ef..42e304699cd4 100644 --- a/errorprone/java/com/google/errorprone/bugpatterns/android/EfficientXmlChecker.java +++ b/errorprone/java/com/google/errorprone/bugpatterns/android/EfficientXmlChecker.java @@ -168,6 +168,7 @@ public final class EfficientXmlChecker extends BugChecker */ private static final Matcher CONVERT_PRIMITIVE_TO_STRING = new Matcher() { + @SuppressWarnings("TreeToString") //TODO: Fix me @Override public boolean matches(ExpressionTree tree, VisitorState state) { if (PRIMITIVE_TO_STRING.matches(tree, state)) { @@ -205,6 +206,7 @@ public boolean matches(ExpressionTree tree, VisitorState state) { */ private static final Matcher CONVERT_STRING_TO_PRIMITIVE = new Matcher() { + @SuppressWarnings("TreeToString") //TODO: Fix me @Override public boolean matches(ExpressionTree tree, VisitorState state) { if (PRIMITIVE_PARSE.matches(tree, state)) { diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java index 451b99ea7550..e60d506a171b 100644 --- a/graphics/java/android/graphics/Paint.java +++ b/graphics/java/android/graphics/Paint.java @@ -253,7 +253,7 @@ private static class NoImagePreloadHolder { // These flags are always set on a new/reset paint, even if flags 0 is passed. static final int HIDDEN_DEFAULT_PAINT_FLAGS = DEV_KERN_TEXT_FLAG | EMBEDDED_BITMAP_TEXT_FLAG - | FILTER_BITMAP_FLAG; + | FILTER_BITMAP_FLAG | SUBPIXEL_TEXT_FLAG; /** * Font hinter option that disables font hinting. diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java index a2f5301e353f..722f63c4e0a0 100644 --- a/graphics/java/android/graphics/Typeface.java +++ b/graphics/java/android/graphics/Typeface.java @@ -29,6 +29,7 @@ import android.annotation.UiThread; import android.compat.annotation.UnsupportedAppUsage; import android.content.res.AssetManager; +import android.content.res.Resources; import android.graphics.fonts.Font; import android.graphics.fonts.FontFamily; import android.graphics.fonts.FontStyle; @@ -67,11 +68,13 @@ import java.io.InputStream; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.reflect.Field; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; @@ -141,6 +144,8 @@ public class Typeface { private static final LruCache sDynamicTypefaceCache = new LruCache<>(16); private static final Object sDynamicCacheLock = new Object(); + // For dynamic default font styles + private static final HashMap sSystemFontOverrides = new HashMap<>(); @GuardedBy("SYSTEM_FONT_MAP_LOCK") static Typeface sDefaultTypeface; @@ -884,7 +889,7 @@ public CustomFallbackBuilder(@NonNull FontFamily family) { * @return The best matching typeface. */ public static Typeface create(String familyName, @Style int style) { - return create(getSystemDefaultTypeface(familyName), style); + return create(getSystemOverrideTypeface(familyName), style); } /** @@ -1176,6 +1181,11 @@ private Typeface(long ni) { mWeight = nativeGetWeight(ni); } + private static Typeface getSystemOverrideTypeface(@NonNull String familyName) { + Typeface tf = sSystemFontOverrides.get(familyName); + return tf == null ? getSystemDefaultTypeface(familyName) : tf; + } + private static Typeface getSystemDefaultTypeface(@NonNull String familyName) { Typeface tf = sSystemFontMap.get(familyName); return tf == null ? Typeface.DEFAULT : tf; @@ -1345,6 +1355,60 @@ public static void setSystemFontMap(@Nullable SharedMemory sharedMemory) } } + private static void setPublicDefaults(String familyName) { + synchronized (SYSTEM_FONT_MAP_LOCK) { + sDefaults = new Typeface[] { + DEFAULT, + DEFAULT_BOLD, + create(getSystemDefaultTypeface(familyName), Typeface.ITALIC), + create(getSystemDefaultTypeface(familyName), Typeface.BOLD_ITALIC), + }; + } + } + + private static void setFinalField(String fieldName, Typeface value) { + synchronized (SYSTEM_FONT_MAP_LOCK) { + try { + Field field = Typeface.class.getDeclaredField(fieldName); + // isAccessible bypasses final on ART + field.setAccessible(true); + field.set(null, value); + field.setAccessible(false); + } catch (NoSuchFieldException | IllegalAccessException e) { + Log.e(TAG, "Failed to set Typeface." + fieldName, e); + } + } + } + + /** @hide */ + public static void updateDefaultFont(Resources res) { + synchronized (SYSTEM_FONT_MAP_LOCK) { + String familyName = res.getString(com.android.internal.R.string.config_bodyFontFamily); + Typeface typeface = sSystemFontMap.get(familyName); + if (typeface == null) { + // This should never happen, but if the system font family name is invalid, just return + // instead of crashing the app. + return; + } + + setDefault(typeface); + + // Static typefaces in public API + setFinalField("DEFAULT", create(getSystemDefaultTypeface(familyName), 0)); + setFinalField("DEFAULT_BOLD", create(getSystemDefaultTypeface(familyName), Typeface.BOLD)); + setFinalField("SANS_SERIF", DEFAULT); + + // For default aliases used in framework styles + sSystemFontOverrides.put("sans-serif", typeface); + sSystemFontOverrides.put("sans-serif-thin", create(typeface, 100, false)); + sSystemFontOverrides.put("sans-serif-light", create(typeface, 300, false)); + sSystemFontOverrides.put("sans-serif-medium", create(typeface, 500, false)); + sSystemFontOverrides.put("sans-serif-black", create(typeface, 900, false)); + + setPublicDefaults(familyName); + } + } + /** @hide */ @VisibleForTesting public static void setSystemFontMap(Map systemFontMap) { @@ -1365,12 +1429,7 @@ public static void setSystemFontMap(Map systemFontMap) { nativeForceSetStaticFinalField("SERIF", create("serif", 0)); nativeForceSetStaticFinalField("MONOSPACE", create("monospace", 0)); - sDefaults = new Typeface[]{ - DEFAULT, - DEFAULT_BOLD, - create((String) null, Typeface.ITALIC), - create((String) null, Typeface.BOLD_ITALIC), - }; + setPublicDefaults(null); // A list of generic families to be registered in native. // https://www.w3.org/TR/css-fonts-4/#generic-font-families diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java index 417a27d0f506..14dc6a2513a0 100644 --- a/graphics/java/android/graphics/drawable/RippleDrawable.java +++ b/graphics/java/android/graphics/drawable/RippleDrawable.java @@ -762,7 +762,7 @@ private void onHotspotBoundsChanged() { if (mBackground != null) { mBackground.onHotspotBoundsChanged(); } - float newRadius = Math.round(getComputedRadius()); + float newRadius = getComputedRadius(); for (int i = 0; i < mRunningAnimations.size(); i++) { RippleAnimationSession s = mRunningAnimations.get(i); s.setRadius(newRadius); diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java index 4065bd110c7e..e25ee906b410 100644 --- a/graphics/java/android/graphics/drawable/VectorDrawable.java +++ b/graphics/java/android/graphics/drawable/VectorDrawable.java @@ -60,7 +60,7 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.ArrayList; -import java.util.HashMap; +import java.util.Map; import java.util.Stack; /** @@ -1171,18 +1171,14 @@ static class VGroup extends VObject { private static final int NATIVE_ALLOCATION_SIZE = 100; - private static final HashMap sPropertyIndexMap = - new HashMap() { - { - put("translateX", TRANSLATE_X_INDEX); - put("translateY", TRANSLATE_Y_INDEX); - put("scaleX", SCALE_X_INDEX); - put("scaleY", SCALE_Y_INDEX); - put("pivotX", PIVOT_X_INDEX); - put("pivotY", PIVOT_Y_INDEX); - put("rotation", ROTATION_INDEX); - } - }; + private static final Map sPropertyIndexMap = Map.of( + "translateX", TRANSLATE_X_INDEX, + "translateY", TRANSLATE_Y_INDEX, + "scaleX", SCALE_X_INDEX, + "scaleY", SCALE_Y_INDEX, + "pivotX", PIVOT_X_INDEX, + "pivotY", PIVOT_Y_INDEX, + "rotation", ROTATION_INDEX); static int getPropertyIndex(String propertyName) { if (sPropertyIndexMap.containsKey(propertyName)) { @@ -1285,18 +1281,15 @@ public Float get(VGroup object) { } }; - private static final HashMap sPropertyMap = - new HashMap() { - { - put("translateX", TRANSLATE_X); - put("translateY", TRANSLATE_Y); - put("scaleX", SCALE_X); - put("scaleY", SCALE_Y); - put("pivotX", PIVOT_X); - put("pivotY", PIVOT_Y); - put("rotation", ROTATION); - } - }; + private static final Map sPropertyMap = Map.of( + "translateX", TRANSLATE_X, + "translateY", TRANSLATE_Y, + "scaleX", SCALE_X, + "scaleY", SCALE_Y, + "pivotX", PIVOT_X, + "pivotY", PIVOT_Y, + "rotation", ROTATION); + // Temp array to store transform values obtained from native. private float[] mTransform; ///////////////////////////////////////////////////// @@ -1762,19 +1755,15 @@ static class VFullPath extends VPath { private static final int NATIVE_ALLOCATION_SIZE = 264; // Property map for animatable attributes. - private final static HashMap sPropertyIndexMap - = new HashMap () { - { - put("strokeWidth", STROKE_WIDTH_INDEX); - put("strokeColor", STROKE_COLOR_INDEX); - put("strokeAlpha", STROKE_ALPHA_INDEX); - put("fillColor", FILL_COLOR_INDEX); - put("fillAlpha", FILL_ALPHA_INDEX); - put("trimPathStart", TRIM_PATH_START_INDEX); - put("trimPathEnd", TRIM_PATH_END_INDEX); - put("trimPathOffset", TRIM_PATH_OFFSET_INDEX); - } - }; + private static final Map sPropertyIndexMap = Map.of( + "strokeWidth", STROKE_WIDTH_INDEX, + "strokeColor", STROKE_COLOR_INDEX, + "strokeAlpha", STROKE_ALPHA_INDEX, + "fillColor", FILL_COLOR_INDEX, + "fillAlpha", FILL_ALPHA_INDEX, + "trimPathStart", TRIM_PATH_START_INDEX, + "trimPathEnd", TRIM_PATH_END_INDEX, + "trimPathOffset", TRIM_PATH_OFFSET_INDEX); // Below are the Properties that wrap the setters to avoid reflection overhead in animations private static final Property STROKE_WIDTH = @@ -1881,19 +1870,15 @@ public Float get(VFullPath object) { } }; - private final static HashMap sPropertyMap - = new HashMap () { - { - put("strokeWidth", STROKE_WIDTH); - put("strokeColor", STROKE_COLOR); - put("strokeAlpha", STROKE_ALPHA); - put("fillColor", FILL_COLOR); - put("fillAlpha", FILL_ALPHA); - put("trimPathStart", TRIM_PATH_START); - put("trimPathEnd", TRIM_PATH_END); - put("trimPathOffset", TRIM_PATH_OFFSET); - } - }; + private static final Map sPropertyMap = Map.of( + "strokeWidth", STROKE_WIDTH, + "strokeColor", STROKE_COLOR, + "strokeAlpha", STROKE_ALPHA, + "fillColor", FILL_COLOR, + "fillAlpha", FILL_ALPHA, + "trimPathStart", TRIM_PATH_START, + "trimPathEnd", TRIM_PATH_END, + "trimPathOffset", TRIM_PATH_OFFSET); // Temp array to store property data obtained from native getter. private byte[] mPropertyData; diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java index 33411e1ec5b9..88717cfff0e9 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java @@ -77,6 +77,8 @@ import javax.crypto.SecretKey; +import com.android.internal.util.custom.PixelPropsUtils; + /** * A java.security.KeyStore interface for the Android KeyStore. An instance of * it can be created via the {@link java.security.KeyStore#getInstance(String) @@ -164,6 +166,8 @@ private KeyEntryResponse getKeyMetadata(String alias) { @Override public Certificate[] engineGetCertificateChain(String alias) { + PixelPropsUtils.onEngineGetCertificateChain(); + KeyEntryResponse response = getKeyMetadata(alias); if (response == null || response.metadata.certificate == null) { diff --git a/libs/WindowManager/Shell/res/layout/one_handed_tutorial.xml b/libs/WindowManager/Shell/res/layout/one_handed_tutorial.xml index d29ed8b5a9ec..8835289c163e 100644 --- a/libs/WindowManager/Shell/res/layout/one_handed_tutorial.xml +++ b/libs/WindowManager/Shell/res/layout/one_handed_tutorial.xml @@ -42,7 +42,7 @@ android:layout_marginBottom="0dp" android:gravity="center_horizontal" android:textAlignment="center" - android:fontFamily="google-sans-medium" + android:fontFamily="@*android:string/config_headlineFontFamily" android:text="@string/one_handed_tutorial_title" android:textSize="16sp" android:textColor="@android:color/white"/> diff --git a/libs/WindowManager/Shell/res/layout/tv_pip_menu_action_button.xml b/libs/WindowManager/Shell/res/layout/tv_pip_menu_action_button.xml index db96d8de4094..a02251058430 100644 --- a/libs/WindowManager/Shell/res/layout/tv_pip_menu_action_button.xml +++ b/libs/WindowManager/Shell/res/layout/tv_pip_menu_action_button.xml @@ -37,4 +37,4 @@ android:layout_gravity="center" android:duplicateParentState="true" android:tint="@color/tv_pip_menu_icon" /> - \ No newline at end of file + diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java index f170e774739f..99388c9f204c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java @@ -653,8 +653,13 @@ public void onTaskAppeared(ActivityManager.RunningTaskInfo info, SurfaceControl if (mOneShotAnimationType == ANIM_TYPE_BOUNDS) { mPipMenuController.attach(mLeash); - final Rect sourceHintRect = PipBoundsAlgorithm.getValidSourceHintRect( + Rect sourceHintRect = PipBoundsAlgorithm.getValidSourceHintRect( info.pictureInPictureParams, currentBounds); + if (sourceHintRect != null && currentBounds != null + && sourceHintRect.width() < currentBounds.width() / 2 + && sourceHintRect.height() < currentBounds.height() / 3) { + sourceHintRect = null; + } scheduleAnimateResizePip(currentBounds, destinationBounds, 0 /* startingAngle */, sourceHintRect, TRANSITION_DIRECTION_TO_PIP, mEnterAnimationDuration, null /* updateBoundsCallback */); diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp index 5e8a623d4205..5825fe88a5c9 100644 --- a/libs/androidfw/ResourceTypes.cpp +++ b/libs/androidfw/ResourceTypes.cpp @@ -4387,7 +4387,7 @@ bool ResTable::getResourceName(uint32_t resID, bool allowUtf8, resource_name* ou if (p < 0) { if (Res_GETPACKAGE(resID)+1 == 0) { - ALOGW("No package identifier when getting name for resource number 0x%08x", resID); + ALOGV("No package identifier when getting name for resource number 0x%08x", resID); } else { #ifndef STATIC_ANDROIDFW_FOR_TOOLS ALOGW("No known package when getting name for resource number 0x%08x", resID); diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index ad9aa6cdd3d9..33f79352b8b8 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -674,6 +674,7 @@ cc_test { srcs: [ "tests/unit/main.cpp", "tests/unit/ABitmapTests.cpp", + "tests/unit/AutoBackendTextureReleaseTests.cpp", "tests/unit/CacheManagerTests.cpp", "tests/unit/CanvasContextTests.cpp", "tests/unit/CanvasOpTests.cpp", diff --git a/libs/hwui/AutoBackendTextureRelease.cpp b/libs/hwui/AutoBackendTextureRelease.cpp index ef5eacbdb4ad..b656b6ac8204 100644 --- a/libs/hwui/AutoBackendTextureRelease.cpp +++ b/libs/hwui/AutoBackendTextureRelease.cpp @@ -32,9 +32,17 @@ AutoBackendTextureRelease::AutoBackendTextureRelease(GrDirectContext* context, bool createProtectedImage = 0 != (desc.usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT); GrBackendFormat backendFormat = GrAHardwareBufferUtils::GetBackendFormat(context, buffer, desc.format, false); + LOG_ALWAYS_FATAL_IF(!backendFormat.isValid(), + __FILE__ " Invalid GrBackendFormat. GrBackendApi==%" PRIu32 + ", AHardwareBuffer_Format==%" PRIu32 ".", + static_cast(context->backend()), desc.format); mBackendTexture = GrAHardwareBufferUtils::MakeBackendTexture( context, buffer, desc.width, desc.height, &mDeleteProc, &mUpdateProc, &mImageCtx, createProtectedImage, backendFormat, false); + LOG_ALWAYS_FATAL_IF(!mBackendTexture.isValid(), + __FILE__ " Invalid GrBackendTexture. Width==%" PRIu32 ", height==%" PRIu32 + ", protected==%d", + desc.width, desc.height, createProtectedImage); } void AutoBackendTextureRelease::unref(bool releaseImage) { @@ -74,13 +82,13 @@ void AutoBackendTextureRelease::makeImage(AHardwareBuffer* buffer, AHardwareBuffer_Desc desc; AHardwareBuffer_describe(buffer, &desc); SkColorType colorType = GrAHardwareBufferUtils::GetSkColorTypeFromBufferFormat(desc.format); + // The following ref will be counteracted by Skia calling releaseProc, either during + // MakeFromTexture if there is a failure, or later when SkImage is discarded. It must + // be called before MakeFromTexture, otherwise Skia may remove HWUI's ref on failure. + ref(); mImage = SkImage::MakeFromTexture( context, mBackendTexture, kTopLeft_GrSurfaceOrigin, colorType, kPremul_SkAlphaType, uirenderer::DataSpaceToColorSpace(dataspace), releaseProc, this); - if (mImage.get()) { - // The following ref will be counteracted by releaseProc, when SkImage is discarded. - ref(); - } } void AutoBackendTextureRelease::newBufferContent(GrDirectContext* context) { diff --git a/libs/hwui/AutoBackendTextureRelease.h b/libs/hwui/AutoBackendTextureRelease.h index c9bb767a3185..f0eb2a8b6eab 100644 --- a/libs/hwui/AutoBackendTextureRelease.h +++ b/libs/hwui/AutoBackendTextureRelease.h @@ -25,6 +25,9 @@ namespace android { namespace uirenderer { +// Friend TestUtils serves as a proxy for any test cases that require access to private members. +class TestUtils; + /** * AutoBackendTextureRelease manages EglImage/VkImage lifetime. It is a ref-counted object * that keeps GPU resources alive until the last SkImage object using them is destroyed. @@ -66,6 +69,9 @@ class AutoBackendTextureRelease final { // mImage is the SkImage created from mBackendTexture. sk_sp mImage; + + // Friend TestUtils serves as a proxy for any test cases that require access to private members. + friend class TestUtils; }; } /* namespace uirenderer */ diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h index 5092675a8104..fcaa745e9fc6 100644 --- a/libs/hwui/tests/common/TestUtils.h +++ b/libs/hwui/tests/common/TestUtils.h @@ -16,6 +16,7 @@ #pragma once +#include #include #include #include @@ -283,6 +284,11 @@ class TestUtils { static SkRect getClipBounds(const SkCanvas* canvas); static SkRect getLocalClipBounds(const SkCanvas* canvas); + static int getUsageCount(const AutoBackendTextureRelease* textureRelease) { + EXPECT_NE(nullptr, textureRelease); + return textureRelease->mUsageCount; + } + struct CallCounts { int sync = 0; int contextDestroyed = 0; diff --git a/libs/hwui/tests/unit/AutoBackendTextureReleaseTests.cpp b/libs/hwui/tests/unit/AutoBackendTextureReleaseTests.cpp new file mode 100644 index 000000000000..2ec78a429481 --- /dev/null +++ b/libs/hwui/tests/unit/AutoBackendTextureReleaseTests.cpp @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "AutoBackendTextureRelease.h" +#include "tests/common/TestUtils.h" + +using namespace android; +using namespace android::uirenderer; + +AHardwareBuffer* allocHardwareBuffer() { + AHardwareBuffer* buffer; + AHardwareBuffer_Desc desc = { + .width = 16, + .height = 16, + .layers = 1, + .format = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, + .usage = AHARDWAREBUFFER_USAGE_CPU_READ_RARELY | AHARDWAREBUFFER_USAGE_CPU_WRITE_RARELY, + }; + constexpr int kSucceeded = 0; + int status = AHardwareBuffer_allocate(&desc, &buffer); + EXPECT_EQ(kSucceeded, status); + return buffer; +} + +// Expands to AutoBackendTextureRelease_makeImage_invalid_RenderThreadTest, +// set as friend in AutoBackendTextureRelease.h +RENDERTHREAD_TEST(AutoBackendTextureRelease, makeImage_invalid) { + AHardwareBuffer* buffer = allocHardwareBuffer(); + AutoBackendTextureRelease* textureRelease = + new AutoBackendTextureRelease(renderThread.getGrContext(), buffer); + + EXPECT_EQ(1, TestUtils::getUsageCount(textureRelease)); + + // SkImage::MakeFromTexture should fail if given null GrDirectContext. + textureRelease->makeImage(buffer, HAL_DATASPACE_UNKNOWN, /*context = */ nullptr); + + EXPECT_EQ(1, TestUtils::getUsageCount(textureRelease)); + + textureRelease->unref(true); + AHardwareBuffer_release(buffer); +} + +// Expands to AutoBackendTextureRelease_makeImage_valid_RenderThreadTest, +// set as friend in AutoBackendTextureRelease.h +RENDERTHREAD_TEST(AutoBackendTextureRelease, makeImage_valid) { + AHardwareBuffer* buffer = allocHardwareBuffer(); + AutoBackendTextureRelease* textureRelease = + new AutoBackendTextureRelease(renderThread.getGrContext(), buffer); + + EXPECT_EQ(1, TestUtils::getUsageCount(textureRelease)); + + textureRelease->makeImage(buffer, HAL_DATASPACE_UNKNOWN, renderThread.getGrContext()); + + EXPECT_EQ(2, TestUtils::getUsageCount(textureRelease)); + + textureRelease->unref(true); + AHardwareBuffer_release(buffer); +} diff --git a/media/java/android/media/AppVolume.java b/media/java/android/media/AppVolume.java new file mode 100644 index 000000000000..fac8a02a60d5 --- /dev/null +++ b/media/java/android/media/AppVolume.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2022 The Kaleidoscope Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media; + +import android.annotation.NonNull; + +/** + * @hide + */ +public class AppVolume { + private final String mPackageName; + private final float mVolume; + private final boolean mMute; + private final boolean mActive; + + AppVolume(String packageName, boolean mute, float volume, boolean active) { + mPackageName = packageName; + mMute = mute; + mVolume = volume; + mActive = active; + } + + @NonNull + public String getPackageName() { + return mPackageName; + } + + public float getVolume() { + return mVolume; + } + + public boolean isMuted() { + return mMute; + } + + public boolean isActive() { + return mActive; + } +} diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java index ded9597b68ef..5ead6af42c6e 100644 --- a/media/java/android/media/AudioAttributes.java +++ b/media/java/android/media/AudioAttributes.java @@ -712,12 +712,6 @@ public boolean isContentSpatialized() { */ @CapturePolicy public int getAllowedCapturePolicy() { - if ((mFlags & FLAG_NO_SYSTEM_CAPTURE) == FLAG_NO_SYSTEM_CAPTURE) { - return ALLOW_CAPTURE_BY_NONE; - } - if ((mFlags & FLAG_NO_MEDIA_PROJECTION) == FLAG_NO_MEDIA_PROJECTION) { - return ALLOW_CAPTURE_BY_SYSTEM; - } return ALLOW_CAPTURE_BY_ALL; } @@ -1766,20 +1760,7 @@ private static int toVolumeStreamType(boolean fromGetVolumeControlStream, AudioA * @hide */ public static int capturePolicyToFlags(@CapturePolicy int capturePolicy, int flags) { - switch (capturePolicy) { - case ALLOW_CAPTURE_BY_NONE: - flags |= FLAG_NO_MEDIA_PROJECTION | FLAG_NO_SYSTEM_CAPTURE; - break; - case ALLOW_CAPTURE_BY_SYSTEM: - flags |= FLAG_NO_MEDIA_PROJECTION; - flags &= ~FLAG_NO_SYSTEM_CAPTURE; - break; - case ALLOW_CAPTURE_BY_ALL: - flags &= ~FLAG_NO_SYSTEM_CAPTURE & ~FLAG_NO_MEDIA_PROJECTION; - break; - default: - throw new IllegalArgumentException("Unknown allow playback capture policy"); - } + flags &= ~FLAG_NO_SYSTEM_CAPTURE & ~FLAG_NO_MEDIA_PROJECTION; return flags; } diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java index d06bbc61f6eb..2ecaf23759d9 100644 --- a/media/java/android/media/AudioFormat.java +++ b/media/java/android/media/AudioFormat.java @@ -355,6 +355,31 @@ public final class AudioFormat implements Parcelable { /** Audio data format: DRA compressed */ public static final int ENCODING_DRA = 28; + /** Audio data format: AMRNB + * @hide + * */ + public static final int ENCODING_AMRNB = 100; + /** Audio data format: AMRWB + * @hide + * */ + public static final int ENCODING_AMRWB = 101; + /** Audio data format: EVRC + * @hide + * */ + public static final int ENCODING_EVRC = 102; + /** Audio data format: EVRCB + * @hide + * */ + public static final int ENCODING_EVRCB = 103; + /** Audio data format: EVRCWB + * @hide + * */ + public static final int ENCODING_EVRCWB = 104; + /** Audio data format: EVRCNW + * @hide + * */ + public static final int ENCODING_EVRCNW = 105; + /** @hide */ public static String toLogFriendlyEncoding(int enc) { switch(enc) { @@ -715,6 +740,7 @@ public static int convertNativeChannelMaskToOutMask(int nativeMask) { | CHANNEL_IN_BACK_RIGHT | CHANNEL_IN_LOW_FREQUENCY); /** @hide */ public static final int CHANNEL_IN_FRONT_BACK = CHANNEL_IN_FRONT | CHANNEL_IN_BACK; + // CHANNEL_IN_ALL is not yet defined; if added then it should match AUDIO_CHANNEL_IN_ALL /** @hide */ @@ -733,6 +759,15 @@ public static int getBytesPerSample(int audioFormat) case ENCODING_PCM_FLOAT: case ENCODING_PCM_32BIT: return 4; + case ENCODING_AMRNB: + return 32; + case ENCODING_AMRWB: + return 61; + case ENCODING_EVRC: + case ENCODING_EVRCB: + case ENCODING_EVRCWB: + case ENCODING_EVRCNW: + return 23; case ENCODING_INVALID: default: throw new IllegalArgumentException("Bad audio format " + audioFormat); @@ -770,6 +805,12 @@ public static boolean isValidEncoding(int audioFormat) case ENCODING_MPEGH_LC_L4: case ENCODING_DTS_UHD: case ENCODING_DRA: + case ENCODING_AMRNB: + case ENCODING_AMRWB: + case ENCODING_EVRC: + case ENCODING_EVRCB: + case ENCODING_EVRCWB: + case ENCODING_EVRCNW: return true; default: return false; @@ -847,6 +888,12 @@ public static boolean isEncodingLinearPcm(int audioFormat) case ENCODING_MPEGH_LC_L4: case ENCODING_DTS_UHD: case ENCODING_DRA: + case ENCODING_AMRNB: + case ENCODING_AMRWB: + case ENCODING_EVRC: + case ENCODING_EVRCB: + case ENCODING_EVRCWB: + case ENCODING_EVRCNW: return false; case ENCODING_INVALID: default: @@ -1175,6 +1222,12 @@ public Builder setEncoding(@Encoding int encoding) throws IllegalArgumentExcepti case ENCODING_MPEGH_LC_L4: case ENCODING_DTS_UHD: case ENCODING_DRA: + case ENCODING_AMRNB: + case ENCODING_AMRWB: + case ENCODING_EVRC: + case ENCODING_EVRCB: + case ENCODING_EVRCWB: + case ENCODING_EVRCNW: mEncoding = encoding; break; case ENCODING_INVALID: @@ -1403,7 +1456,13 @@ public String toString () { ENCODING_MPEGH_LC_L3, ENCODING_MPEGH_LC_L4, ENCODING_DTS_UHD, - ENCODING_DRA } + ENCODING_DRA, + ENCODING_AMRNB, + ENCODING_AMRWB, + ENCODING_EVRC, + ENCODING_EVRCB, + ENCODING_EVRCWB, + ENCODING_EVRCNW } ) @Retention(RetentionPolicy.SOURCE) public @interface Encoding {} diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index e7eda3ea4552..4ec82723f3da 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -1036,6 +1036,32 @@ public void setMasterMute(boolean mute, int flags) { } } + /** @hide */ + @UnsupportedAppUsage + @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) + public int setAppVolume(String packageName, float volume) { + return AudioSystem.setAppVolume(packageName, volume); + } + + /** @hide */ + @UnsupportedAppUsage + @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) + public int setAppMute(String packageName, boolean mute) { + return AudioSystem.setAppMute(packageName, mute); + } + + /** @hide */ + @UnsupportedAppUsage + @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) + public ArrayList listAppVolumes() { + ArrayList volumes = new ArrayList(); + int status = AudioSystem.listAppVolumes(volumes); + if (status != AudioManager.SUCCESS) { + return new ArrayList(); + } + return volumes; + } + /** * Returns the current ringtone mode. * @@ -2325,10 +2351,9 @@ private int removeOnDevRoleForCapturePresetChangedListener( return AudioSystem.SUCCESS; } - private final Map mDevRoleForCapturePresetListeners = new HashMap<>(){{ - put(AudioSystem.DEVICE_ROLE_PREFERRED, - new DevRoleListeners()); - }}; + private final Map mDevRoleForCapturePresetListeners = Map.of( + AudioSystem.DEVICE_ROLE_PREFERRED, + new DevRoleListeners()); private class DevRoleListenerInfo { final @NonNull Executor mExecutor; @@ -6483,15 +6508,17 @@ public void unregisterAudioPortUpdateListener(OnAudioPortUpdateListener l) { // AudioPort implementation // - static final int AUDIOPORT_GENERATION_INIT = 0; - static Integer sAudioPortGeneration = new Integer(AUDIOPORT_GENERATION_INIT); - static ArrayList sAudioPortsCached = new ArrayList(); - static ArrayList sPreviousAudioPortsCached = new ArrayList(); - static ArrayList sAudioPatchesCached = new ArrayList(); + private static final int AUDIOPORT_GENERATION_INIT = 0; + private static Object sAudioPortGenerationLock = new Object(); + @GuardedBy("sAudioPortGenerationLock") + private static int sAudioPortGeneration = AUDIOPORT_GENERATION_INIT; + private static ArrayList sAudioPortsCached = new ArrayList(); + private static ArrayList sPreviousAudioPortsCached = new ArrayList(); + private static ArrayList sAudioPatchesCached = new ArrayList(); static int resetAudioPortGeneration() { int generation; - synchronized (sAudioPortGeneration) { + synchronized (sAudioPortGenerationLock) { generation = sAudioPortGeneration; sAudioPortGeneration = AUDIOPORT_GENERATION_INIT; } @@ -6501,7 +6528,7 @@ static int resetAudioPortGeneration() { static int updateAudioPortCache(ArrayList ports, ArrayList patches, ArrayList previousPorts) { sAudioPortEventHandler.init(); - synchronized (sAudioPortGeneration) { + synchronized (sAudioPortGenerationLock) { if (sAudioPortGeneration == AUDIOPORT_GENERATION_INIT) { int[] patchGeneration = new int[1]; diff --git a/media/java/android/media/AudioMetadata.java b/media/java/android/media/AudioMetadata.java index ca175b4853a6..0f962f9e9d4b 100644 --- a/media/java/android/media/AudioMetadata.java +++ b/media/java/android/media/AudioMetadata.java @@ -30,6 +30,7 @@ import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.HashSet; +import java.util.Map; import java.util.Objects; import java.util.Set; @@ -446,14 +447,13 @@ private static Object getValueFromValuePair(@Nullable Pair, Object> value // BaseMap is corresponding to audio_utils::metadata::Data private static final int AUDIO_METADATA_OBJ_TYPE_BASEMAP = 6; - private static final HashMap AUDIO_METADATA_OBJ_TYPES = new HashMap<>() {{ - put(Integer.class, AUDIO_METADATA_OBJ_TYPE_INT); - put(Long.class, AUDIO_METADATA_OBJ_TYPE_LONG); - put(Float.class, AUDIO_METADATA_OBJ_TYPE_FLOAT); - put(Double.class, AUDIO_METADATA_OBJ_TYPE_DOUBLE); - put(String.class, AUDIO_METADATA_OBJ_TYPE_STRING); - put(BaseMap.class, AUDIO_METADATA_OBJ_TYPE_BASEMAP); - }}; + private static final Map AUDIO_METADATA_OBJ_TYPES = Map.of( + Integer.class, AUDIO_METADATA_OBJ_TYPE_INT, + Long.class, AUDIO_METADATA_OBJ_TYPE_LONG, + Float.class, AUDIO_METADATA_OBJ_TYPE_FLOAT, + Double.class, AUDIO_METADATA_OBJ_TYPE_DOUBLE, + String.class, AUDIO_METADATA_OBJ_TYPE_STRING, + BaseMap.class, AUDIO_METADATA_OBJ_TYPE_BASEMAP); private static final Charset AUDIO_METADATA_CHARSET = StandardCharsets.UTF_8; @@ -634,8 +634,8 @@ default Class getMyType() { * Datum corresponds to Object ****************************************************************************************/ - private static final HashMap> DATA_PACKAGES = new HashMap<>() {{ - put(AUDIO_METADATA_OBJ_TYPE_INT, new DataPackage() { + private static final Map> DATA_PACKAGES = Map.of( + AUDIO_METADATA_OBJ_TYPE_INT, new DataPackage() { @Override @Nullable public Integer unpack(ByteBuffer buffer) { @@ -647,8 +647,8 @@ public boolean pack(AutoGrowByteBuffer output, Integer obj) { output.putInt(obj); return true; } - }); - put(AUDIO_METADATA_OBJ_TYPE_LONG, new DataPackage() { + }, + AUDIO_METADATA_OBJ_TYPE_LONG, new DataPackage() { @Override @Nullable public Long unpack(ByteBuffer buffer) { @@ -660,8 +660,8 @@ public boolean pack(AutoGrowByteBuffer output, Long obj) { output.putLong(obj); return true; } - }); - put(AUDIO_METADATA_OBJ_TYPE_FLOAT, new DataPackage() { + }, + AUDIO_METADATA_OBJ_TYPE_FLOAT, new DataPackage() { @Override @Nullable public Float unpack(ByteBuffer buffer) { @@ -673,8 +673,8 @@ public boolean pack(AutoGrowByteBuffer output, Float obj) { output.putFloat(obj); return true; } - }); - put(AUDIO_METADATA_OBJ_TYPE_DOUBLE, new DataPackage() { + }, + AUDIO_METADATA_OBJ_TYPE_DOUBLE, new DataPackage() { @Override @Nullable public Double unpack(ByteBuffer buffer) { @@ -686,8 +686,8 @@ public boolean pack(AutoGrowByteBuffer output, Double obj) { output.putDouble(obj); return true; } - }); - put(AUDIO_METADATA_OBJ_TYPE_STRING, new DataPackage() { + }, + AUDIO_METADATA_OBJ_TYPE_STRING, new DataPackage() { @Override @Nullable public String unpack(ByteBuffer buffer) { @@ -713,9 +713,9 @@ public boolean pack(AutoGrowByteBuffer output, String obj) { output.put(valueArr); return true; } - }); - put(AUDIO_METADATA_OBJ_TYPE_BASEMAP, new BaseMapPackage()); - }}; + }, + AUDIO_METADATA_OBJ_TYPE_BASEMAP, new BaseMapPackage()); + // ObjectPackage is a special case that it is expected to unpack audio_utils::metadata::Datum, // which contains data type and data size besides the payload for the data. private static final ObjectPackage OBJECT_PACKAGE = new ObjectPackage(); diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java index 8be1bcdc9479..34e8c6f21857 100644 --- a/media/java/android/media/AudioRecord.java +++ b/media/java/android/media/AudioRecord.java @@ -1055,6 +1055,12 @@ private void audioParamCheck(int audioSource, int sampleRateInHz, int audioForma case AudioFormat.ENCODING_PCM_FLOAT: case AudioFormat.ENCODING_PCM_16BIT: case AudioFormat.ENCODING_PCM_8BIT: + case AudioFormat.ENCODING_AMRNB: + case AudioFormat.ENCODING_AMRWB: + case AudioFormat.ENCODING_EVRC: + case AudioFormat.ENCODING_EVRCB: + case AudioFormat.ENCODING_EVRCWB: + case AudioFormat.ENCODING_EVRCNW: mAudioFormat = audioFormat; break; default: @@ -1298,6 +1304,9 @@ static public int getMinBufferSize(int sampleRateInHz, int channelConfig, int au case (AudioFormat.CHANNEL_IN_FRONT | AudioFormat.CHANNEL_IN_BACK): channelCount = 2; break; + case AudioFormat.CHANNEL_IN_5POINT1: + channelCount = 6; + break; case AudioFormat.CHANNEL_INVALID: default: loge("getMinBufferSize(): Invalid channel configuration."); diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java index 7ccbe51bbd49..b261f516f012 100644 --- a/media/java/android/media/AudioSystem.java +++ b/media/java/android/media/AudioSystem.java @@ -1758,6 +1758,13 @@ public static String deviceSetToString(@NonNull Set devices) { return sb.toString(); } + /** @hide */ + public static native int setAppVolume(@NonNull String packageName, float volume); + /** @hide */ + public static native int setAppMute(@NonNull String packageName, boolean mute); + /** @hide */ + public static native int listAppVolumes(ArrayList volumes); + /** * @hide * Do not use directly, see {@link AudioManager#getDevicesForAttributes(AudioAttributes)} diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java index 85cd342b5e11..d51f1e1327bd 100644 --- a/media/java/android/media/AudioTrack.java +++ b/media/java/android/media/AudioTrack.java @@ -48,8 +48,8 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.NioUtils; -import java.util.HashMap; import java.util.LinkedList; +import java.util.Map; import java.util.Objects; import java.util.concurrent.Executor; @@ -1867,26 +1867,24 @@ private void audioParamCheck(int sampleRateInHz, int channelConfig, int channelI } // General pair map - private static final HashMap CHANNEL_PAIR_MAP = new HashMap<>() {{ - put("front", AudioFormat.CHANNEL_OUT_FRONT_LEFT - | AudioFormat.CHANNEL_OUT_FRONT_RIGHT); - put("back", AudioFormat.CHANNEL_OUT_BACK_LEFT - | AudioFormat.CHANNEL_OUT_BACK_RIGHT); - put("front of center", AudioFormat.CHANNEL_OUT_FRONT_LEFT_OF_CENTER - | AudioFormat.CHANNEL_OUT_FRONT_RIGHT_OF_CENTER); - put("side", AudioFormat.CHANNEL_OUT_SIDE_LEFT - | AudioFormat.CHANNEL_OUT_SIDE_RIGHT); - put("top front", AudioFormat.CHANNEL_OUT_TOP_FRONT_LEFT - | AudioFormat.CHANNEL_OUT_TOP_FRONT_RIGHT); - put("top back", AudioFormat.CHANNEL_OUT_TOP_BACK_LEFT - | AudioFormat.CHANNEL_OUT_TOP_BACK_RIGHT); - put("top side", AudioFormat.CHANNEL_OUT_TOP_SIDE_LEFT - | AudioFormat.CHANNEL_OUT_TOP_SIDE_RIGHT); - put("bottom front", AudioFormat.CHANNEL_OUT_BOTTOM_FRONT_LEFT - | AudioFormat.CHANNEL_OUT_BOTTOM_FRONT_RIGHT); - put("front wide", AudioFormat.CHANNEL_OUT_FRONT_WIDE_LEFT - | AudioFormat.CHANNEL_OUT_FRONT_WIDE_RIGHT); - }}; + private static final Map CHANNEL_PAIR_MAP = Map.of( + "front", AudioFormat.CHANNEL_OUT_FRONT_LEFT + | AudioFormat.CHANNEL_OUT_FRONT_RIGHT, + "back", AudioFormat.CHANNEL_OUT_BACK_LEFT + | AudioFormat.CHANNEL_OUT_BACK_RIGHT, + "front of center", AudioFormat.CHANNEL_OUT_FRONT_LEFT_OF_CENTER + | AudioFormat.CHANNEL_OUT_FRONT_RIGHT_OF_CENTER, + "side", AudioFormat.CHANNEL_OUT_SIDE_LEFT | AudioFormat.CHANNEL_OUT_SIDE_RIGHT, + "top front", AudioFormat.CHANNEL_OUT_TOP_FRONT_LEFT + | AudioFormat.CHANNEL_OUT_TOP_FRONT_RIGHT, + "top back", AudioFormat.CHANNEL_OUT_TOP_BACK_LEFT + | AudioFormat.CHANNEL_OUT_TOP_BACK_RIGHT, + "top side", AudioFormat.CHANNEL_OUT_TOP_SIDE_LEFT + | AudioFormat.CHANNEL_OUT_TOP_SIDE_RIGHT, + "bottom front", AudioFormat.CHANNEL_OUT_BOTTOM_FRONT_LEFT + | AudioFormat.CHANNEL_OUT_BOTTOM_FRONT_RIGHT, + "front wide", AudioFormat.CHANNEL_OUT_FRONT_WIDE_LEFT + | AudioFormat.CHANNEL_OUT_FRONT_WIDE_RIGHT); /** * Convenience method to check that the channel configuration (a.k.a channel mask) is supported @@ -1924,7 +1922,7 @@ private static boolean isMultichannelConfigSupported(int channelConfig, int enco return false; } // Check all pairs to see that they are matched (front duplicated here). - for (HashMap.Entry e : CHANNEL_PAIR_MAP.entrySet()) { + for (Map.Entry e : CHANNEL_PAIR_MAP.entrySet()) { final int positionPair = e.getValue(); if ((channelConfig & positionPair) != 0 && (channelConfig & positionPair) != positionPair) { diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java index 0c8cacd894cf..524bde4e7d15 100644 --- a/media/java/android/media/ExifInterface.java +++ b/media/java/android/media/ExifInterface.java @@ -70,6 +70,7 @@ import java.util.HashSet; import java.util.Locale; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.TimeZone; import java.util.regex.Matcher; @@ -4836,12 +4837,13 @@ private static Pair guessDataFormat(String entryValue) { for (int i = 1; i < entryValues.length; ++i) { final Pair guessDataFormat = guessDataFormat(entryValues[i]); int first = -1, second = -1; - if (guessDataFormat.first == dataFormat.first - || guessDataFormat.second == dataFormat.first) { + if (Objects.equals(guessDataFormat.first, dataFormat.first) + || Objects.equals(guessDataFormat.second, dataFormat.first)) { first = dataFormat.first; } - if (dataFormat.second != -1 && (guessDataFormat.first == dataFormat.second - || guessDataFormat.second == dataFormat.second)) { + if (dataFormat.second != -1 + && (Objects.equals(guessDataFormat.first, dataFormat.second) + || Objects.equals(guessDataFormat.second, dataFormat.second))) { second = dataFormat.second; } if (first == -1 && second == -1) { diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java index 75fd64a4ccf5..bf30c5032132 100644 --- a/media/java/android/media/MediaCodecInfo.java +++ b/media/java/android/media/MediaCodecInfo.java @@ -1445,7 +1445,7 @@ private void applyLevelLimits() { sampleRates = new int[] { 8000, 12000, 16000, 24000, 48000 }; maxChannels = 255; } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_RAW)) { - sampleRateRange = Range.create(1, 96000); + sampleRateRange = Range.create(1, 192000); bitRates = Range.create(1, 10000000); maxChannels = AudioSystem.OUT_CHANNEL_COUNT_MAX; } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_FLAC)) { diff --git a/media/java/android/media/MediaHTTPService.java b/media/java/android/media/MediaHTTPService.java index 3008067daefb..2342a426e77c 100644 --- a/media/java/android/media/MediaHTTPService.java +++ b/media/java/android/media/MediaHTTPService.java @@ -21,6 +21,8 @@ import android.os.IBinder; import android.util.Log; +import com.android.internal.annotations.GuardedBy; + import java.net.CookieHandler; import java.net.CookieManager; import java.net.CookieStore; @@ -31,7 +33,9 @@ public class MediaHTTPService extends IMediaHTTPService.Stub { private static final String TAG = "MediaHTTPService"; @Nullable private List mCookies; - private Boolean mCookieStoreInitialized = new Boolean(false); + private final Object mCookieStoreInitializedLock = new Object(); + @GuardedBy("mCookieStoreInitializedLock") + private boolean mCookieStoreInitialized = false; public MediaHTTPService(@Nullable List cookies) { mCookies = cookies; @@ -40,7 +44,7 @@ public MediaHTTPService(@Nullable List cookies) { public IMediaHTTPConnection makeHTTPConnection() { - synchronized (mCookieStoreInitialized) { + synchronized (mCookieStoreInitializedLock) { // Only need to do it once for all connections if ( !mCookieStoreInitialized ) { CookieHandler cookieHandler = CookieHandler.getDefault(); @@ -78,8 +82,8 @@ public IMediaHTTPConnection makeHTTPConnection() { Log.v(TAG, "makeHTTPConnection(" + this + "): cookieHandler: " + cookieHandler + " Cookies: " + mCookies); - } // mCookieStoreInitialized - } // synchronized + } + } return new MediaHTTPConnection(); } diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java index 10772754bd6c..78771cef5f78 100644 --- a/media/java/android/media/MediaPlayer.java +++ b/media/java/android/media/MediaPlayer.java @@ -2473,6 +2473,8 @@ private native void native_setup(Object mediaplayerThis, * * @see android.media.MediaPlayer#getTrackInfo */ + // The creator needs to be pulic, which requires removing the @UnsupportedAppUsage + @SuppressWarnings("ParcelableCreator") static public class TrackInfo implements Parcelable { /** * Gets the track type. diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java index f1e60387340e..b00f78e2ee9b 100644 --- a/media/java/android/media/MediaRecorder.java +++ b/media/java/android/media/MediaRecorder.java @@ -627,6 +627,13 @@ private OutputFormat() {} /** Opus data in a Ogg container */ public static final int OGG = 11; + + /** @hide QCP file format */ + public static final int QCP = 20; + + /** @hide WAVE media file format*/ + public static final int WAVE = 21; + }; /** @@ -672,6 +679,12 @@ private AudioEncoder() {} public static final int VORBIS = 6; /** Opus audio codec */ public static final int OPUS = 7; + /** @hide EVRC audio codec */ + public static final int EVRC = 10; + /** @hide QCELP audio codec */ + public static final int QCELP = 11; + /** @hide Linear PCM audio codec */ + public static final int LPCM = 12; } /** diff --git a/media/java/android/media/RingtoneManager.java b/media/java/android/media/RingtoneManager.java index 27db41cb9f4e..52360f8ed92f 100644 --- a/media/java/android/media/RingtoneManager.java +++ b/media/java/android/media/RingtoneManager.java @@ -50,6 +50,8 @@ import android.provider.MediaStore.MediaColumns; import android.provider.Settings; import android.provider.Settings.System; +import android.telephony.SubscriptionManager; +import android.text.TextUtils; import android.util.Log; import com.android.internal.database.SortCursor; @@ -151,6 +153,7 @@ public class RingtoneManager { * for this {@link Uri}. If showing an item for "Default" (@see * {@link #EXTRA_RINGTONE_SHOW_DEFAULT}), this can also 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} to have the "Default" item * checked. @@ -164,7 +167,7 @@ public class RingtoneManager { * Given to the ringtone picker as a {@link Uri}. The {@link Uri} of the * ringtone to play when the user attempts to preview the "Default" * ringtone. This can be one of {@link System#DEFAULT_RINGTONE_URI}, - * {@link System#DEFAULT_NOTIFICATION_URI}, or + * {@link System#DEFAULT_RINGTONE2_URI}, {@link System#DEFAULT_NOTIFICATION_URI}, or * {@link System#DEFAULT_ALARM_ALERT_URI} to have the "Default" point to * the current sound for the given default sound type. If you are showing a * ringtone picker for some other type of sound, you are free to provide any @@ -203,7 +206,7 @@ public class RingtoneManager { * It will be one of: *

  • the picked ringtone, *
  • a {@link Uri} that equals {@link System#DEFAULT_RINGTONE_URI}, - * {@link System#DEFAULT_NOTIFICATION_URI}, or + * {@link System#DEFAULT_RINGTONE2_URI}, {@link System#DEFAULT_NOTIFICATION_URI}, or * {@link System#DEFAULT_ALARM_ALERT_URI} if the default was chosen, *
  • null if the "Silent" item was picked. * @@ -789,8 +792,8 @@ private static Ringtone getRingtone(final Context context, Uri ringtoneUri, int /** * Gets the current default sound's {@link Uri}. This will give the actual * sound {@link Uri}, instead of using this, most clients can use - * {@link System#DEFAULT_RINGTONE_URI}. - * + * {@link System#DEFAULT_RINGTONE_URI} or {@link System#DEFAULT_RINGTONE2_URI}. + * * @param context A context used for querying. * @param type The type whose default sound should be returned. One of * {@link #TYPE_RINGTONE}, {@link #TYPE_NOTIFICATION}, or @@ -799,7 +802,27 @@ private static Ringtone getRingtone(final Context context, Uri ringtoneUri, int * @see #setActualDefaultRingtoneUri(Context, int, Uri) */ public static Uri getActualDefaultRingtoneUri(Context context, int type) { - String setting = getSettingForType(type); + return getActualDefaultRingtoneUriBySlot(context, type, + SubscriptionManager.getDefaultVoicePhoneId()); + } + + /** + * Gets the current default sound's {@link Uri} by slotId. This will give the actual + * sound {@link Uri}, instead of using this, most clients can use + * {@link System#DEFAULT_RINGTONE_URI} or {@link System#DEFAULT_RINGTONE2_URI}. + * + * @param context A context used for querying. + * @param type The type whose default sound should be returned. One of + * {@link #TYPE_RINGTONE}, {@link #TYPE_NOTIFICATION}, or + * {@link #TYPE_ALARM}. + * @param slotId The slotId whose default sound should be returned. + * @return A {@link Uri} pointing to the default sound for the sound type. + * @see #setActualDefaultRingtoneUriBySlot(Context, int, Uri, int) + * + * @hide + */ + public static Uri getActualDefaultRingtoneUriBySlot(Context context, int type, int slotId) { + String setting = getSettingForTypeBySlot(type, slotId); if (setting == null) return null; final String uriString = Settings.System.getStringForUser(context.getContentResolver(), setting, context.getUserId()); @@ -817,7 +840,7 @@ public static Uri getActualDefaultRingtoneUri(Context context, int type) { /** * Sets the {@link Uri} of the default sound for a given sound type. - * + * * @param context A context used for querying. * @param type The type whose default sound should be set. One of * {@link #TYPE_RINGTONE}, {@link #TYPE_NOTIFICATION}, or @@ -826,7 +849,26 @@ public static Uri getActualDefaultRingtoneUri(Context context, int type) { * @see #getActualDefaultRingtoneUri(Context, int) */ public static void setActualDefaultRingtoneUri(Context context, int type, Uri ringtoneUri) { - String setting = getSettingForType(type); + setActualDefaultRingtoneUriBySlot(context, type, ringtoneUri, + SubscriptionManager.getDefaultVoicePhoneId()); + } + + /** + * Sets the {@link Uri} of the default sound by slotId for a given sound type. + * + * @param context A context used for querying. + * @param type The type whose default sound should be set. One of + * {@link #TYPE_RINGTONE}, {@link #TYPE_NOTIFICATION}, or + * {@link #TYPE_ALARM}. + * @param ringtoneUri A {@link Uri} pointing to the default sound to set. + * @param slotId The slotId whose default sound should be set. + * @see #getActualDefaultRingtoneUriBySlot(Context, int, int) + * + * @hide + */ + public static void setActualDefaultRingtoneUriBySlot(Context context, int type, + Uri ringtoneUri, int slotId) { + String setting = getSettingForTypeBySlot(type, slotId); if (setting == null) return; final ContentResolver resolver = context.getContentResolver(); @@ -839,7 +881,7 @@ public static void setActualDefaultRingtoneUri(Context context, int type, Uri ri // Stream selected ringtone into cache so it's available for playback // when CE storage is still locked if (ringtoneUri != null) { - final Uri cacheUri = getCacheForType(type, context.getUserId()); + final Uri cacheUri = getCacheForTypeBySlot(type, context.getUserId(), slotId); try (InputStream in = openRingtone(context, ringtoneUri); OutputStream out = resolver.openOutputStream(cacheUri)) { FileUtils.copy(in, out); @@ -964,9 +1006,9 @@ private static InputStream openRingtone(Context context, Uri uri) throws IOExcep } } - private static String getSettingForType(int type) { + private static String getSettingForTypeBySlot(int type, int slotId) { if ((type & TYPE_RINGTONE) != 0) { - return Settings.System.RINGTONE; + return slotId == 1 ? Settings.System.RINGTONE2 : Settings.System.RINGTONE; } else if ((type & TYPE_NOTIFICATION) != 0) { return Settings.System.NOTIFICATION_SOUND; } else if ((type & TYPE_ALARM) != 0) { @@ -978,13 +1020,22 @@ private static String getSettingForType(int type) { /** {@hide} */ public static Uri getCacheForType(int type) { - return getCacheForType(type, UserHandle.getCallingUserId()); + return getCacheForTypeBySlot(type, UserHandle.getCallingUserId(), + SubscriptionManager.getDefaultVoicePhoneId()); } /** {@hide} */ public static Uri getCacheForType(int type, int userId) { + return getCacheForTypeBySlot(type, userId, SubscriptionManager.getDefaultVoicePhoneId()); + } + + /** {@hide} */ + public static Uri getCacheForTypeBySlot(int type, int userId, int slotId) { if ((type & TYPE_RINGTONE) != 0) { - return ContentProvider.maybeAddUserId(Settings.System.RINGTONE_CACHE_URI, userId); + Uri ringtoneUri = slotId == 1 + ? Settings.System.RINGTONE2_CACHE_URI + : Settings.System.RINGTONE_CACHE_URI; + return ContentProvider.maybeAddUserId(ringtoneUri, userId); } else if ((type & TYPE_NOTIFICATION) != 0) { return ContentProvider.maybeAddUserId(Settings.System.NOTIFICATION_SOUND_CACHE_URI, userId); @@ -1009,6 +1060,7 @@ public static boolean isDefault(Uri ringtoneUri) { * * @param defaultRingtoneUri The default {@link Uri}. For example, * {@link System#DEFAULT_RINGTONE_URI}, + * {@link System#DEFAULT_RINGTONE2_URI}, * {@link System#DEFAULT_NOTIFICATION_URI}, or * {@link System#DEFAULT_ALARM_ALERT_URI}. * @return The type of the defaultRingtoneUri, or -1. @@ -1017,7 +1069,8 @@ public static int getDefaultType(Uri defaultRingtoneUri) { defaultRingtoneUri = ContentProvider.getUriWithoutUserId(defaultRingtoneUri); if (defaultRingtoneUri == null) { return -1; - } else if (defaultRingtoneUri.equals(Settings.System.DEFAULT_RINGTONE_URI)) { + } else if (defaultRingtoneUri.equals(Settings.System.DEFAULT_RINGTONE_URI) + || defaultRingtoneUri.equals(Settings.System.DEFAULT_RINGTONE2_URI)) { return TYPE_RINGTONE; } else if (defaultRingtoneUri.equals(Settings.System.DEFAULT_NOTIFICATION_URI)) { return TYPE_NOTIFICATION; @@ -1033,13 +1086,31 @@ public static int getDefaultType(Uri defaultRingtoneUri) { * Rather than returning the actual ringtone's sound {@link Uri}, this will * return the symbolic {@link Uri} which will resolved to the actual sound * when played. - * + * * @param type The ringtone type whose default should be returned. * @return The {@link Uri} of the default ringtone for the given type. */ public static Uri getDefaultUri(int type) { + return getDefaultUriBySlot(type, SubscriptionManager.getDefaultVoicePhoneId()); + } + + /** + * Returns the {@link Uri} for the default ringtone by slotId of a particular type. + * Rather than returning the actual ringtone's sound {@link Uri}, this will + * return the symbolic {@link Uri} which will resolved to the actual sound + * when played. + * + * @param type The ringtone type whose default should be returned. + * @param slotId The slotId whose default should be returned. + * @return The {@link Uri} of the default ringtone for the given type. + * + * @hide + */ + public static Uri getDefaultUriBySlot(int type, int slotId) { if ((type & TYPE_RINGTONE) != 0) { - return Settings.System.DEFAULT_RINGTONE_URI; + return slotId == 1 + ? Settings.System.DEFAULT_RINGTONE2_URI + : Settings.System.DEFAULT_RINGTONE_URI; } else if ((type & TYPE_NOTIFICATION) != 0) { return Settings.System.DEFAULT_NOTIFICATION_URI; } else if ((type & TYPE_ALARM) != 0) { @@ -1153,8 +1224,11 @@ public static void ensureDefaultRingtones(@NonNull Context context) { // Skip if we've already defined it at least once, so we don't // overwrite the user changing to null final String setting = getDefaultRingtoneSetting(type); + String defaultRingtone2 = Settings.System.getString(context.getContentResolver(), Settings.System.RINGTONE2); if (Settings.System.getInt(context.getContentResolver(), setting, 0) != 0) { - continue; + if (!TextUtils.isEmpty(defaultRingtone2)) { + continue; + } } // Try finding the scanned ringtone @@ -1170,6 +1244,9 @@ public static void ensureDefaultRingtones(@NonNull Context context) { final Uri ringtoneUri = context.getContentResolver().canonicalizeOrElse( ContentUris.withAppendedId(baseUri, cursor.getLong(0))); RingtoneManager.setActualDefaultRingtoneUri(context, type, ringtoneUri); + if (TextUtils.isEmpty(defaultRingtone2)) { + RingtoneManager.setActualDefaultRingtoneUriBySlot(context, TYPE_RINGTONE, ringtoneUri, 1); + } Settings.System.putInt(context.getContentResolver(), setting, 1); } } diff --git a/media/java/android/media/tv/tuner/dvr/DvrRecorder.java b/media/java/android/media/tv/tuner/dvr/DvrRecorder.java index e72026aab992..1aae8f413559 100644 --- a/media/java/android/media/tv/tuner/dvr/DvrRecorder.java +++ b/media/java/android/media/tv/tuner/dvr/DvrRecorder.java @@ -27,6 +27,7 @@ import android.os.Process; import android.util.Log; +import com.android.internal.annotations.GuardedBy; import com.android.internal.util.FrameworkStatsLog; import java.util.concurrent.Executor; @@ -47,7 +48,9 @@ public class DvrRecorder implements AutoCloseable { private static int sInstantId = 0; private int mSegmentId = 0; private int mOverflow; - private Boolean mIsStopped = true; + private final Object mIsStoppedLock = new Object(); + @GuardedBy("mIsStoppedLock") + private boolean mIsStopped = true; private final Object mListenerLock = new Object(); private native int nativeAttachFilter(Filter filter); @@ -147,7 +150,7 @@ public int start() { .write(FrameworkStatsLog.TV_TUNER_DVR_STATUS, mUserId, FrameworkStatsLog.TV_TUNER_DVR_STATUS__TYPE__RECORD, FrameworkStatsLog.TV_TUNER_DVR_STATUS__STATE__STARTED, mSegmentId, 0); - synchronized (mIsStopped) { + synchronized (mIsStoppedLock) { int result = nativeStartDvr(); if (result == Tuner.RESULT_SUCCESS) { mIsStopped = false; @@ -170,7 +173,7 @@ public int stop() { .write(FrameworkStatsLog.TV_TUNER_DVR_STATUS, mUserId, FrameworkStatsLog.TV_TUNER_DVR_STATUS__TYPE__RECORD, FrameworkStatsLog.TV_TUNER_DVR_STATUS__STATE__STOPPED, mSegmentId, mOverflow); - synchronized (mIsStopped) { + synchronized (mIsStoppedLock) { int result = nativeStopDvr(); if (result == Tuner.RESULT_SUCCESS) { mIsStopped = true; @@ -188,7 +191,7 @@ public int stop() { */ @Result public int flush() { - synchronized (mIsStopped) { + synchronized (mIsStoppedLock) { if (mIsStopped) { return nativeFlushDvr(); } diff --git a/media/java/android/mtp/MtpPropertyGroup.java b/media/java/android/mtp/MtpPropertyGroup.java index aff2e1b4cf31..89e5e0d783ff 100644 --- a/media/java/android/mtp/MtpPropertyGroup.java +++ b/media/java/android/mtp/MtpPropertyGroup.java @@ -230,7 +230,7 @@ public int getPropertyList(ContentProviderClient provider, String volumeName, case MtpConstants.PROPERTY_PERSISTENT_UID: // The persistent uid must be unique and never reused among all objects, // and remain the same between sessions. - long puid = (object.getPath().toString().hashCode() << 32) + long puid = (((long) object.getPath().toString().hashCode()) << 32) + object.getModifiedTime(); list.append(id, property.code, property.type, puid); break; diff --git a/media/mca/effect/java/android/media/effect/EffectFactory.java b/media/mca/effect/java/android/media/effect/EffectFactory.java index f6fcba71e339..cbb273608ad7 100644 --- a/media/mca/effect/java/android/media/effect/EffectFactory.java +++ b/media/mca/effect/java/android/media/effect/EffectFactory.java @@ -486,11 +486,9 @@ private static Class getEffectClassByName(String className) { private Effect instantiateEffect(Class effectClass, String name) { // Make sure this is an Effect subclass - try { - effectClass.asSubclass(Effect.class); - } catch (ClassCastException e) { + if (!Effect.class.isAssignableFrom(effectClass)) { throw new IllegalArgumentException("Attempting to allocate effect '" + effectClass - + "' which is not a subclass of Effect!", e); + + "' which is not a subclass of Effect!"); } // Look for the correct constructor diff --git a/media/mca/filterfw/java/android/filterfw/core/Filter.java b/media/mca/filterfw/java/android/filterfw/core/Filter.java index a608ef5be3f4..e82c046b7390 100644 --- a/media/mca/filterfw/java/android/filterfw/core/Filter.java +++ b/media/mca/filterfw/java/android/filterfw/core/Filter.java @@ -90,9 +90,7 @@ public static final boolean isAvailable(String filterName) { return false; } // Then make sure it's a subclass of Filter. - try { - filterClass.asSubclass(Filter.class); - } catch (ClassCastException e) { + if (!Filter.class.isAssignableFrom(filterClass)) { return false; } return true; diff --git a/media/mca/filterfw/java/android/filterfw/core/FilterFactory.java b/media/mca/filterfw/java/android/filterfw/core/FilterFactory.java index 779df990a9a5..736e51131ca1 100644 --- a/media/mca/filterfw/java/android/filterfw/core/FilterFactory.java +++ b/media/mca/filterfw/java/android/filterfw/core/FilterFactory.java @@ -112,9 +112,7 @@ public Filter createFilterByClassName(String className, String filterName) { public Filter createFilterByClass(Class filterClass, String filterName) { // Make sure this is a Filter subclass - try { - filterClass.asSubclass(Filter.class); - } catch (ClassCastException e) { + if (!Filter.class.isAssignableFrom(filterClass)) { throw new IllegalArgumentException("Attempting to allocate class '" + filterClass + "' which is not a subclass of Filter!"); } diff --git a/media/mca/filterfw/java/android/filterfw/core/KeyValueMap.java b/media/mca/filterfw/java/android/filterfw/core/KeyValueMap.java index 8cf9a13438e5..6ff1885fa8eb 100644 --- a/media/mca/filterfw/java/android/filterfw/core/KeyValueMap.java +++ b/media/mca/filterfw/java/android/filterfw/core/KeyValueMap.java @@ -55,12 +55,12 @@ public String getString(String key) { public int getInt(String key) { Object result = get(key); - return result != null ? (Integer)result : null; + return result != null ? (Integer) result : 0; } public float getFloat(String key) { Object result = get(key); - return result != null ? (Float)result : null; + return result != null ? (Float) result : 0; } @Override diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java index c52816571ffb..8c05725a9ef1 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java @@ -296,7 +296,7 @@ public void getMemoryWriteToLog(int writeCount) { mMemWriter.write("End Memory :" + mEndMemory + "\n"); } } catch (Exception e) { - e.toString(); + // TODO } } diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java index 39add7ea8a42..c814eba7d187 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java @@ -264,8 +264,14 @@ private static String formatArray(T array, int badIndex) { builder.append("**"); } - if (elem instanceof Number) { - builder.append(String.format("%x", elem)); + if (elem instanceof Byte) { + builder.append(String.format("%x", (Byte) elem)); + } else if (elem instanceof Short) { + builder.append(String.format("%x", (Short) elem)); + } else if (elem instanceof Integer) { + builder.append(String.format("%x", (Integer) elem)); + } else if (elem instanceof Long) { + builder.append(String.format("%x", (Long) elem)); } else { builder.append(elem); } diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerGetCurrentPositionStateUnitTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerGetCurrentPositionStateUnitTest.java index fd1c2d3e8e94..37dd4b5330c5 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerGetCurrentPositionStateUnitTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerGetCurrentPositionStateUnitTest.java @@ -18,7 +18,7 @@ import android.media.MediaPlayer; import android.test.AndroidTestCase; -import android.test.suitebuilder.annotation.LargeTest;; +import android.test.suitebuilder.annotation.LargeTest; /** * Unit test class to test the set of valid and invalid states that diff --git a/media/tests/contents/media_api/HEVC_320_AAC_128.mp4 b/media/tests/contents/media_api/HEVC_320_AAC_128.mp4 deleted file mode 100644 index e3e3ef0148a1..000000000000 Binary files a/media/tests/contents/media_api/HEVC_320_AAC_128.mp4 and /dev/null differ diff --git a/media/tests/contents/media_api/goldenThumbnail.png b/media/tests/contents/media_api/goldenThumbnail.png deleted file mode 100644 index 3bb6ed245dc7..000000000000 Binary files a/media/tests/contents/media_api/goldenThumbnail.png and /dev/null differ diff --git a/media/tests/contents/media_api/music/MP3_48KHz_128kbps_s_1_17.mp3 b/media/tests/contents/media_api/music/MP3_48KHz_128kbps_s_1_17.mp3 deleted file mode 100644 index e0d6a1799b3b..000000000000 Binary files a/media/tests/contents/media_api/music/MP3_48KHz_128kbps_s_1_17.mp3 and /dev/null differ diff --git a/media/tests/contents/media_api/music/MP3_48KHz_128kbps_s_1_17_ABR.mp3 b/media/tests/contents/media_api/music/MP3_48KHz_128kbps_s_1_17_ABR.mp3 deleted file mode 100644 index 12f719328600..000000000000 Binary files a/media/tests/contents/media_api/music/MP3_48KHz_128kbps_s_1_17_ABR.mp3 and /dev/null differ diff --git a/media/tests/contents/media_api/music/MP3_48KHz_128kbps_s_1_17_CBR.mp3 b/media/tests/contents/media_api/music/MP3_48KHz_128kbps_s_1_17_CBR.mp3 deleted file mode 100644 index 12f719328600..000000000000 Binary files a/media/tests/contents/media_api/music/MP3_48KHz_128kbps_s_1_17_CBR.mp3 and /dev/null differ diff --git a/media/tests/contents/media_api/music/MP3_48KHz_128kbps_s_1_17_ID3V1.mp3 b/media/tests/contents/media_api/music/MP3_48KHz_128kbps_s_1_17_ID3V1.mp3 deleted file mode 100644 index 29d332b7ba13..000000000000 Binary files a/media/tests/contents/media_api/music/MP3_48KHz_128kbps_s_1_17_ID3V1.mp3 and /dev/null differ diff --git a/media/tests/contents/media_api/music/MP3_48KHz_128kbps_s_1_17_ID3V1_ID3V2.mp3 b/media/tests/contents/media_api/music/MP3_48KHz_128kbps_s_1_17_ID3V1_ID3V2.mp3 deleted file mode 100644 index ea5263827df8..000000000000 Binary files a/media/tests/contents/media_api/music/MP3_48KHz_128kbps_s_1_17_ID3V1_ID3V2.mp3 and /dev/null differ diff --git a/media/tests/contents/media_api/music/MP3_48KHz_128kbps_s_1_17_ID3V2.mp3 b/media/tests/contents/media_api/music/MP3_48KHz_128kbps_s_1_17_ID3V2.mp3 deleted file mode 100644 index 024039c82d8c..000000000000 Binary files a/media/tests/contents/media_api/music/MP3_48KHz_128kbps_s_1_17_ID3V2.mp3 and /dev/null differ diff --git a/media/tests/contents/media_api/music/MP3_48KHz_128kbps_s_1_17_VBR.mp3 b/media/tests/contents/media_api/music/MP3_48KHz_128kbps_s_1_17_VBR.mp3 deleted file mode 100644 index 12f719328600..000000000000 Binary files a/media/tests/contents/media_api/music/MP3_48KHz_128kbps_s_1_17_VBR.mp3 and /dev/null differ diff --git a/media/tests/contents/media_api/music/SHORTMP3.mp3 b/media/tests/contents/media_api/music/SHORTMP3.mp3 deleted file mode 100644 index 8b51b5d9f932..000000000000 Binary files a/media/tests/contents/media_api/music/SHORTMP3.mp3 and /dev/null differ diff --git a/media/tests/contents/media_api/music/ants.mid b/media/tests/contents/media_api/music/ants.mid deleted file mode 100644 index d4ead5314181..000000000000 Binary files a/media/tests/contents/media_api/music/ants.mid and /dev/null differ diff --git a/media/tests/contents/media_api/music/bzk_chic.wav b/media/tests/contents/media_api/music/bzk_chic.wav deleted file mode 100644 index bab1a6b4bfb5..000000000000 Binary files a/media/tests/contents/media_api/music/bzk_chic.wav and /dev/null differ diff --git a/media/tests/contents/media_api/music/sine_200+1000Hz_44K_mo.wav b/media/tests/contents/media_api/music/sine_200+1000Hz_44K_mo.wav deleted file mode 100644 index 24c2a0d4b2f4..000000000000 Binary files a/media/tests/contents/media_api/music/sine_200+1000Hz_44K_mo.wav and /dev/null differ diff --git a/media/tests/contents/media_api/music/test_amr_ietf.amr b/media/tests/contents/media_api/music/test_amr_ietf.amr deleted file mode 100644 index 540794cf573c..000000000000 Binary files a/media/tests/contents/media_api/music/test_amr_ietf.amr and /dev/null differ diff --git a/media/tests/contents/media_api/video/H263_500_AMRNB_12.3gp b/media/tests/contents/media_api/video/H263_500_AMRNB_12.3gp deleted file mode 100644 index 46bb2b1511de..000000000000 Binary files a/media/tests/contents/media_api/video/H263_500_AMRNB_12.3gp and /dev/null differ diff --git a/media/tests/contents/media_api/video/H263_56_AAC_24.3gp b/media/tests/contents/media_api/video/H263_56_AAC_24.3gp deleted file mode 100644 index 1fb11925fee8..000000000000 Binary files a/media/tests/contents/media_api/video/H263_56_AAC_24.3gp and /dev/null differ diff --git a/media/tests/contents/media_api/video/H263_56_AMRNB_6.3gp b/media/tests/contents/media_api/video/H263_56_AMRNB_6.3gp deleted file mode 100644 index b6eb6a18982f..000000000000 Binary files a/media/tests/contents/media_api/video/H263_56_AMRNB_6.3gp and /dev/null differ diff --git a/media/tests/contents/media_api/video/H264_320_AAC_64.3gp b/media/tests/contents/media_api/video/H264_320_AAC_64.3gp deleted file mode 100644 index 04680ce3c2be..000000000000 Binary files a/media/tests/contents/media_api/video/H264_320_AAC_64.3gp and /dev/null differ diff --git a/media/tests/contents/media_api/video/H264_320_AMRNB_6.3gp b/media/tests/contents/media_api/video/H264_320_AMRNB_6.3gp deleted file mode 100644 index bc533a221958..000000000000 Binary files a/media/tests/contents/media_api/video/H264_320_AMRNB_6.3gp and /dev/null differ diff --git a/media/tests/contents/media_api/video/H264_500_AAC_128.3gp b/media/tests/contents/media_api/video/H264_500_AAC_128.3gp deleted file mode 100644 index 05d67eaeed92..000000000000 Binary files a/media/tests/contents/media_api/video/H264_500_AAC_128.3gp and /dev/null differ diff --git a/media/tests/contents/media_api/video/H264_HVGA_500_NO_AUDIO.3gp b/media/tests/contents/media_api/video/H264_HVGA_500_NO_AUDIO.3gp deleted file mode 100644 index 13642b25ef1e..000000000000 Binary files a/media/tests/contents/media_api/video/H264_HVGA_500_NO_AUDIO.3gp and /dev/null differ diff --git a/media/tests/contents/media_api/video/H264_QVGA_500_NO_AUDIO.3gp b/media/tests/contents/media_api/video/H264_QVGA_500_NO_AUDIO.3gp deleted file mode 100644 index 13642b25ef1e..000000000000 Binary files a/media/tests/contents/media_api/video/H264_QVGA_500_NO_AUDIO.3gp and /dev/null differ diff --git a/media/tests/contents/media_api/video/MPEG2_1500_AAC_128.mp4 b/media/tests/contents/media_api/video/MPEG2_1500_AAC_128.mp4 deleted file mode 100644 index 33f66a087603..000000000000 Binary files a/media/tests/contents/media_api/video/MPEG2_1500_AAC_128.mp4 and /dev/null differ diff --git a/media/tests/contents/media_api/video/MPEG4_320_AAC_64.mp4 b/media/tests/contents/media_api/video/MPEG4_320_AAC_64.mp4 deleted file mode 100644 index 90f185606d0f..000000000000 Binary files a/media/tests/contents/media_api/video/MPEG4_320_AAC_64.mp4 and /dev/null differ diff --git a/media/tests/contents/media_api/video/big-buck-bunny_trailer.webm b/media/tests/contents/media_api/video/big-buck-bunny_trailer.webm deleted file mode 100644 index 6a17395f67d3..000000000000 Binary files a/media/tests/contents/media_api/video/big-buck-bunny_trailer.webm and /dev/null differ diff --git a/media/tests/contents/media_api/video/border_large.3gp b/media/tests/contents/media_api/video/border_large.3gp deleted file mode 100644 index e6221604bda4..000000000000 Binary files a/media/tests/contents/media_api/video/border_large.3gp and /dev/null differ diff --git a/media/tests/contents/media_api/videoeditor/AACLC_44.1kHz_256kbps_s_1_17.mp4 b/media/tests/contents/media_api/videoeditor/AACLC_44.1kHz_256kbps_s_1_17.mp4 deleted file mode 100644 index 32d422129b74..000000000000 Binary files a/media/tests/contents/media_api/videoeditor/AACLC_44.1kHz_256kbps_s_1_17.mp4 and /dev/null differ diff --git a/media/tests/contents/media_api/videoeditor/AACLC_48KHz_256Kbps_s_1_17.3gp b/media/tests/contents/media_api/videoeditor/AACLC_48KHz_256Kbps_s_1_17.3gp deleted file mode 100644 index f911cd3874cc..000000000000 Binary files a/media/tests/contents/media_api/videoeditor/AACLC_48KHz_256Kbps_s_1_17.3gp and /dev/null differ diff --git a/media/tests/contents/media_api/videoeditor/AMRNB_8KHz_12.2Kbps_m_1_17.3gp b/media/tests/contents/media_api/videoeditor/AMRNB_8KHz_12.2Kbps_m_1_17.3gp deleted file mode 100644 index f6fccef06867..000000000000 Binary files a/media/tests/contents/media_api/videoeditor/AMRNB_8KHz_12.2Kbps_m_1_17.3gp and /dev/null differ diff --git a/media/tests/contents/media_api/videoeditor/H263_profile0_176x144_10fps_256kbps_0_25.3gp b/media/tests/contents/media_api/videoeditor/H263_profile0_176x144_10fps_256kbps_0_25.3gp deleted file mode 100644 index 593166b2f301..000000000000 Binary files a/media/tests/contents/media_api/videoeditor/H263_profile0_176x144_10fps_256kbps_0_25.3gp and /dev/null differ diff --git a/media/tests/contents/media_api/videoeditor/H263_profile0_176x144_10fps_256kbps_1_17.3gp b/media/tests/contents/media_api/videoeditor/H263_profile0_176x144_10fps_256kbps_1_17.3gp deleted file mode 100644 index 0138d8043ca2..000000000000 Binary files a/media/tests/contents/media_api/videoeditor/H263_profile0_176x144_10fps_256kbps_1_17.3gp and /dev/null differ diff --git a/media/tests/contents/media_api/videoeditor/H263_profile0_176x144_10fps_96kbps_0_25.3gp b/media/tests/contents/media_api/videoeditor/H263_profile0_176x144_10fps_96kbps_0_25.3gp deleted file mode 100644 index 08d97d543e80..000000000000 Binary files a/media/tests/contents/media_api/videoeditor/H263_profile0_176x144_10fps_96kbps_0_25.3gp and /dev/null differ diff --git a/media/tests/contents/media_api/videoeditor/H263_profile0_176x144_15fps_128kbps_1_35.3gp b/media/tests/contents/media_api/videoeditor/H263_profile0_176x144_15fps_128kbps_1_35.3gp deleted file mode 100644 index b73be034dc90..000000000000 Binary files a/media/tests/contents/media_api/videoeditor/H263_profile0_176x144_15fps_128kbps_1_35.3gp and /dev/null differ diff --git a/media/tests/contents/media_api/videoeditor/H263_profile0_176x144_15fps_256kbps_AACLC_16kHz_32kbps_m_0_26.3gp b/media/tests/contents/media_api/videoeditor/H263_profile0_176x144_15fps_256kbps_AACLC_16kHz_32kbps_m_0_26.3gp deleted file mode 100644 index 4bcb3b59d728..000000000000 Binary files a/media/tests/contents/media_api/videoeditor/H263_profile0_176x144_15fps_256kbps_AACLC_16kHz_32kbps_m_0_26.3gp and /dev/null differ diff --git a/media/tests/contents/media_api/videoeditor/H263_profile0_176x144_15fps_256kbps_AACLC_32kHz_128kbps_s_0_26.3gp b/media/tests/contents/media_api/videoeditor/H263_profile0_176x144_15fps_256kbps_AACLC_32kHz_128kbps_s_0_26.3gp deleted file mode 100644 index 0629f386570f..000000000000 Binary files a/media/tests/contents/media_api/videoeditor/H263_profile0_176x144_15fps_256kbps_AACLC_32kHz_128kbps_s_0_26.3gp and /dev/null differ diff --git a/media/tests/contents/media_api/videoeditor/H263_profile0_176x144_15fps_256kbps_AACLC_32kHz_128kbps_s_1_17.3gp b/media/tests/contents/media_api/videoeditor/H263_profile0_176x144_15fps_256kbps_AACLC_32kHz_128kbps_s_1_17.3gp deleted file mode 100644 index c5cd1296bc0a..000000000000 Binary files a/media/tests/contents/media_api/videoeditor/H263_profile0_176x144_15fps_256kbps_AACLC_32kHz_128kbps_s_1_17.3gp and /dev/null differ diff --git a/media/tests/contents/media_api/videoeditor/H264_BP_1080x720_30fps_12Mbps_AACLC_44.1khz_64kbps_s_1_17.mp4 b/media/tests/contents/media_api/videoeditor/H264_BP_1080x720_30fps_12Mbps_AACLC_44.1khz_64kbps_s_1_17.mp4 deleted file mode 100644 index 8486f5515b7d..000000000000 Binary files a/media/tests/contents/media_api/videoeditor/H264_BP_1080x720_30fps_12Mbps_AACLC_44.1khz_64kbps_s_1_17.mp4 and /dev/null differ diff --git a/media/tests/contents/media_api/videoeditor/H264_BP_1080x720_30fps_800kbps_1_17.mp4 b/media/tests/contents/media_api/videoeditor/H264_BP_1080x720_30fps_800kbps_1_17.mp4 deleted file mode 100644 index 217305537f5a..000000000000 Binary files a/media/tests/contents/media_api/videoeditor/H264_BP_1080x720_30fps_800kbps_1_17.mp4 and /dev/null differ diff --git a/media/tests/contents/media_api/videoeditor/H264_BP_1280x1080_30fps_1200Kbps_1_10.mp4 b/media/tests/contents/media_api/videoeditor/H264_BP_1280x1080_30fps_1200Kbps_1_10.mp4 deleted file mode 100644 index 27eab58f6d41..000000000000 Binary files a/media/tests/contents/media_api/videoeditor/H264_BP_1280x1080_30fps_1200Kbps_1_10.mp4 and /dev/null differ diff --git a/media/tests/contents/media_api/videoeditor/H264_BP_1280x720_15fps_512kbps_AACLC_16khz_48kbps_s_1_17.mp4 b/media/tests/contents/media_api/videoeditor/H264_BP_1280x720_15fps_512kbps_AACLC_16khz_48kbps_s_1_17.mp4 deleted file mode 100644 index 457dd96eaa7f..000000000000 Binary files a/media/tests/contents/media_api/videoeditor/H264_BP_1280x720_15fps_512kbps_AACLC_16khz_48kbps_s_1_17.mp4 and /dev/null differ diff --git a/media/tests/contents/media_api/videoeditor/H264_BP_176x144_15fps_144kbps_AMRNB_8kHz_12.2kbps_m_1_17.3gp b/media/tests/contents/media_api/videoeditor/H264_BP_176x144_15fps_144kbps_AMRNB_8kHz_12.2kbps_m_1_17.3gp deleted file mode 100644 index dae2062df477..000000000000 Binary files a/media/tests/contents/media_api/videoeditor/H264_BP_176x144_15fps_144kbps_AMRNB_8kHz_12.2kbps_m_1_17.3gp and /dev/null differ diff --git a/media/tests/contents/media_api/videoeditor/H264_BP_1920x1080_30fps_1200Kbps_1_10.mp4 b/media/tests/contents/media_api/videoeditor/H264_BP_1920x1080_30fps_1200Kbps_1_10.mp4 deleted file mode 100644 index c66ccedced56..000000000000 Binary files a/media/tests/contents/media_api/videoeditor/H264_BP_1920x1080_30fps_1200Kbps_1_10.mp4 and /dev/null differ diff --git a/media/tests/contents/media_api/videoeditor/H264_BP_640x480_12.5fps_256kbps_AACLC_16khz_24kbps_s_0_26.mp4 b/media/tests/contents/media_api/videoeditor/H264_BP_640x480_12.5fps_256kbps_AACLC_16khz_24kbps_s_0_26.mp4 deleted file mode 100644 index e026fa27607d..000000000000 Binary files a/media/tests/contents/media_api/videoeditor/H264_BP_640x480_12.5fps_256kbps_AACLC_16khz_24kbps_s_0_26.mp4 and /dev/null differ diff --git a/media/tests/contents/media_api/videoeditor/H264_BP_640x480_15fps_1200Kbps_AACLC_48KHz_32kbps_m_1_17.3gp b/media/tests/contents/media_api/videoeditor/H264_BP_640x480_15fps_1200Kbps_AACLC_48KHz_32kbps_m_1_17.3gp deleted file mode 100644 index f9e7306c1b97..000000000000 Binary files a/media/tests/contents/media_api/videoeditor/H264_BP_640x480_15fps_1200Kbps_AACLC_48KHz_32kbps_m_1_17.3gp and /dev/null differ diff --git a/media/tests/contents/media_api/videoeditor/H264_BP_640x480_15fps_1200Kbps_AACLC_48KHz_64kps_m_0_27.3gp b/media/tests/contents/media_api/videoeditor/H264_BP_640x480_15fps_1200Kbps_AACLC_48KHz_64kps_m_0_27.3gp deleted file mode 100644 index f9e7306c1b97..000000000000 Binary files a/media/tests/contents/media_api/videoeditor/H264_BP_640x480_15fps_1200Kbps_AACLC_48KHz_64kps_m_0_27.3gp and /dev/null differ diff --git a/media/tests/contents/media_api/videoeditor/H264_BP_640x480_15fps_384kbps_60_0.mp4 b/media/tests/contents/media_api/videoeditor/H264_BP_640x480_15fps_384kbps_60_0.mp4 deleted file mode 100644 index 05224ea07fca..000000000000 Binary files a/media/tests/contents/media_api/videoeditor/H264_BP_640x480_15fps_384kbps_60_0.mp4 and /dev/null differ diff --git a/media/tests/contents/media_api/videoeditor/H264_BP_640x480_30fps_192kbps_1_5.mp4 b/media/tests/contents/media_api/videoeditor/H264_BP_640x480_30fps_192kbps_1_5.mp4 deleted file mode 100644 index 6ac0480bb46f..000000000000 Binary files a/media/tests/contents/media_api/videoeditor/H264_BP_640x480_30fps_192kbps_1_5.mp4 and /dev/null differ diff --git a/media/tests/contents/media_api/videoeditor/H264_BP_640x480_30fps_256kbps_1_17.mp4 b/media/tests/contents/media_api/videoeditor/H264_BP_640x480_30fps_256kbps_1_17.mp4 deleted file mode 100644 index d589bfbfe7d4..000000000000 Binary files a/media/tests/contents/media_api/videoeditor/H264_BP_640x480_30fps_256kbps_1_17.mp4 and /dev/null differ diff --git a/media/tests/contents/media_api/videoeditor/H264_BP_720x480_25fps_256kbps_AMRNB_8khz_12.2kbps_m_0_26.mp4 b/media/tests/contents/media_api/videoeditor/H264_BP_720x480_25fps_256kbps_AMRNB_8khz_12.2kbps_m_0_26.mp4 deleted file mode 100644 index 6bfbe8bdd62b..000000000000 Binary files a/media/tests/contents/media_api/videoeditor/H264_BP_720x480_25fps_256kbps_AMRNB_8khz_12.2kbps_m_0_26.mp4 and /dev/null differ diff --git a/media/tests/contents/media_api/videoeditor/H264_BP_800x480_15fps_512kbps_1_17.mp4 b/media/tests/contents/media_api/videoeditor/H264_BP_800x480_15fps_512kbps_1_17.mp4 deleted file mode 100644 index 4998ccc986a5..000000000000 Binary files a/media/tests/contents/media_api/videoeditor/H264_BP_800x480_15fps_512kbps_1_17.mp4 and /dev/null differ diff --git a/media/tests/contents/media_api/videoeditor/H264_BP_800x480_15fps_512kbps_AACLC_24KHz_38Kbps_s_1_17.mp4 b/media/tests/contents/media_api/videoeditor/H264_BP_800x480_15fps_512kbps_AACLC_24KHz_38Kbps_s_1_17.mp4 deleted file mode 100644 index 6809e7f61c46..000000000000 Binary files a/media/tests/contents/media_api/videoeditor/H264_BP_800x480_15fps_512kbps_AACLC_24KHz_38Kbps_s_1_17.mp4 and /dev/null differ diff --git a/media/tests/contents/media_api/videoeditor/H264_BP_800x480_15fps_512kbps_AMRNB_8KHz_12.2Kbps_m_0_26.mp4 b/media/tests/contents/media_api/videoeditor/H264_BP_800x480_15fps_512kbps_AMRNB_8KHz_12.2Kbps_m_0_26.mp4 deleted file mode 100644 index 74ae62a06345..000000000000 Binary files a/media/tests/contents/media_api/videoeditor/H264_BP_800x480_15fps_512kbps_AMRNB_8KHz_12.2Kbps_m_0_26.mp4 and /dev/null differ diff --git a/media/tests/contents/media_api/videoeditor/H264_BP_960x720_25fps_800kbps_AACLC_48Khz_192Kbps_s_1_17.mp4 b/media/tests/contents/media_api/videoeditor/H264_BP_960x720_25fps_800kbps_AACLC_48Khz_192Kbps_s_1_17.mp4 deleted file mode 100644 index be050dc2a006..000000000000 Binary files a/media/tests/contents/media_api/videoeditor/H264_BP_960x720_25fps_800kbps_AACLC_48Khz_192Kbps_s_1_17.mp4 and /dev/null differ diff --git a/media/tests/contents/media_api/videoeditor/H264_MP_960x720_25fps_800kbps_AACLC_48Khz_192Kbps_s_1_17.mp4 b/media/tests/contents/media_api/videoeditor/H264_MP_960x720_25fps_800kbps_AACLC_48Khz_192Kbps_s_1_17.mp4 deleted file mode 100644 index 178431d4dac6..000000000000 Binary files a/media/tests/contents/media_api/videoeditor/H264_MP_960x720_25fps_800kbps_AACLC_48Khz_192Kbps_s_1_17.mp4 and /dev/null differ diff --git a/media/tests/contents/media_api/videoeditor/IMG_1600x1200.jpg b/media/tests/contents/media_api/videoeditor/IMG_1600x1200.jpg deleted file mode 100644 index b09cb147ad94..000000000000 Binary files a/media/tests/contents/media_api/videoeditor/IMG_1600x1200.jpg and /dev/null differ diff --git a/media/tests/contents/media_api/videoeditor/IMG_176x144.jpg b/media/tests/contents/media_api/videoeditor/IMG_176x144.jpg deleted file mode 100644 index 97a7ba5c32f7..000000000000 Binary files a/media/tests/contents/media_api/videoeditor/IMG_176x144.jpg and /dev/null differ diff --git a/media/tests/contents/media_api/videoeditor/IMG_176x144_Overlay1.png b/media/tests/contents/media_api/videoeditor/IMG_176x144_Overlay1.png deleted file mode 100644 index 147a92521278..000000000000 Binary files a/media/tests/contents/media_api/videoeditor/IMG_176x144_Overlay1.png and /dev/null differ diff --git a/media/tests/contents/media_api/videoeditor/IMG_176x144_Overlay2.png b/media/tests/contents/media_api/videoeditor/IMG_176x144_Overlay2.png deleted file mode 100644 index ba206262f5ea..000000000000 Binary files a/media/tests/contents/media_api/videoeditor/IMG_176x144_Overlay2.png and /dev/null differ diff --git a/media/tests/contents/media_api/videoeditor/IMG_320x240.jpg b/media/tests/contents/media_api/videoeditor/IMG_320x240.jpg deleted file mode 100644 index ec5b5bf96ee1..000000000000 Binary files a/media/tests/contents/media_api/videoeditor/IMG_320x240.jpg and /dev/null differ diff --git a/media/tests/contents/media_api/videoeditor/IMG_640x480.gif b/media/tests/contents/media_api/videoeditor/IMG_640x480.gif deleted file mode 100644 index 19548df8fc77..000000000000 Binary files a/media/tests/contents/media_api/videoeditor/IMG_640x480.gif and /dev/null differ diff --git a/media/tests/contents/media_api/videoeditor/IMG_640x480.jpg b/media/tests/contents/media_api/videoeditor/IMG_640x480.jpg deleted file mode 100644 index c6a96b1e4701..000000000000 Binary files a/media/tests/contents/media_api/videoeditor/IMG_640x480.jpg and /dev/null differ diff --git a/media/tests/contents/media_api/videoeditor/IMG_640x480.png b/media/tests/contents/media_api/videoeditor/IMG_640x480.png deleted file mode 100644 index ba206262f5ea..000000000000 Binary files a/media/tests/contents/media_api/videoeditor/IMG_640x480.png and /dev/null differ diff --git a/media/tests/contents/media_api/videoeditor/IMG_640x480_Overlay1.png b/media/tests/contents/media_api/videoeditor/IMG_640x480_Overlay1.png deleted file mode 100644 index ba206262f5ea..000000000000 Binary files a/media/tests/contents/media_api/videoeditor/IMG_640x480_Overlay1.png and /dev/null differ diff --git a/media/tests/contents/media_api/videoeditor/IMG_640x480_Overlay2.png b/media/tests/contents/media_api/videoeditor/IMG_640x480_Overlay2.png deleted file mode 100644 index 0f32131a632e..000000000000 Binary files a/media/tests/contents/media_api/videoeditor/IMG_640x480_Overlay2.png and /dev/null differ diff --git a/media/tests/contents/media_api/videoeditor/MP3_48KHz_128kbps_s_1_17.mp3 b/media/tests/contents/media_api/videoeditor/MP3_48KHz_128kbps_s_1_17.mp3 deleted file mode 100644 index e0d6a1799b3b..000000000000 Binary files a/media/tests/contents/media_api/videoeditor/MP3_48KHz_128kbps_s_1_17.mp3 and /dev/null differ diff --git a/media/tests/contents/media_api/videoeditor/MPEG2_640x480_30fps_192kbps_1_5.mp4 b/media/tests/contents/media_api/videoeditor/MPEG2_640x480_30fps_192kbps_1_5.mp4 deleted file mode 100644 index 22a92b2f8d48..000000000000 Binary files a/media/tests/contents/media_api/videoeditor/MPEG2_640x480_30fps_192kbps_1_5.mp4 and /dev/null differ diff --git a/media/tests/contents/media_api/videoeditor/MPEG4_SP_176x144_12fps_92kbps_AMRNB_8KHz_12.2kbps_m_0_27.3gp b/media/tests/contents/media_api/videoeditor/MPEG4_SP_176x144_12fps_92kbps_AMRNB_8KHz_12.2kbps_m_0_27.3gp deleted file mode 100644 index a73c4821fb46..000000000000 Binary files a/media/tests/contents/media_api/videoeditor/MPEG4_SP_176x144_12fps_92kbps_AMRNB_8KHz_12.2kbps_m_0_27.3gp and /dev/null differ diff --git a/media/tests/contents/media_api/videoeditor/MPEG4_SP_176x144_30fps_256kbps_AACLC_44.1kHz_96kbps_s_1_17.3gp b/media/tests/contents/media_api/videoeditor/MPEG4_SP_176x144_30fps_256kbps_AACLC_44.1kHz_96kbps_s_1_17.3gp deleted file mode 100644 index 333b88039a7d..000000000000 Binary files a/media/tests/contents/media_api/videoeditor/MPEG4_SP_176x144_30fps_256kbps_AACLC_44.1kHz_96kbps_s_1_17.3gp and /dev/null differ diff --git a/media/tests/contents/media_api/videoeditor/MPEG4_SP_640x480_15fps_1200kbps_AACLC_48khz_64kbps_m_1_17.3gp b/media/tests/contents/media_api/videoeditor/MPEG4_SP_640x480_15fps_1200kbps_AACLC_48khz_64kbps_m_1_17.3gp deleted file mode 100644 index 75a0036368bf..000000000000 Binary files a/media/tests/contents/media_api/videoeditor/MPEG4_SP_640x480_15fps_1200kbps_AACLC_48khz_64kbps_m_1_17.3gp and /dev/null differ diff --git a/media/tests/contents/media_api/videoeditor/MPEG4_SP_640x480_15fps_1200kbps_AACLC_48khz_64kbps_m_1_17.mp4 b/media/tests/contents/media_api/videoeditor/MPEG4_SP_640x480_15fps_1200kbps_AACLC_48khz_64kbps_m_1_17.mp4 deleted file mode 100644 index 75a0036368bf..000000000000 Binary files a/media/tests/contents/media_api/videoeditor/MPEG4_SP_640x480_15fps_1200kbps_AACLC_48khz_64kbps_m_1_17.mp4 and /dev/null differ diff --git a/media/tests/contents/media_api/videoeditor/MPEG4_SP_640x480_15fps_256kbps_0_30.mp4 b/media/tests/contents/media_api/videoeditor/MPEG4_SP_640x480_15fps_256kbps_0_30.mp4 deleted file mode 100644 index be15e907d5ab..000000000000 Binary files a/media/tests/contents/media_api/videoeditor/MPEG4_SP_640x480_15fps_256kbps_0_30.mp4 and /dev/null differ diff --git a/media/tests/contents/media_api/videoeditor/MPEG4_SP_640x480_15fps_512kbps_AACLC_48khz_132kbps_s_0_26.mp4 b/media/tests/contents/media_api/videoeditor/MPEG4_SP_640x480_15fps_512kbps_AACLC_48khz_132kbps_s_0_26.mp4 deleted file mode 100644 index d165d685cb6b..000000000000 Binary files a/media/tests/contents/media_api/videoeditor/MPEG4_SP_640x480_15fps_512kbps_AACLC_48khz_132kbps_s_0_26.mp4 and /dev/null differ diff --git a/media/tests/contents/media_api/videoeditor/MPEG4_SP_640x480_30fps_512Kbps_0_23.3gp b/media/tests/contents/media_api/videoeditor/MPEG4_SP_640x480_30fps_512Kbps_0_23.3gp deleted file mode 100644 index c12f2c86097a..000000000000 Binary files a/media/tests/contents/media_api/videoeditor/MPEG4_SP_640x480_30fps_512Kbps_0_23.3gp and /dev/null differ diff --git a/media/tests/contents/media_api/videoeditor/MPEG4_SP_640x480_30fps_512Kbps_0_27.mp4 b/media/tests/contents/media_api/videoeditor/MPEG4_SP_640x480_30fps_512Kbps_0_27.mp4 deleted file mode 100644 index 13ad5dbc5141..000000000000 Binary files a/media/tests/contents/media_api/videoeditor/MPEG4_SP_640x480_30fps_512Kbps_0_27.mp4 and /dev/null differ diff --git a/media/tests/contents/media_api/videoeditor/MPEG4_SP_720x480_30fps_280kbps_AACLC_48kHz_161kbps_s_0_26.mp4 b/media/tests/contents/media_api/videoeditor/MPEG4_SP_720x480_30fps_280kbps_AACLC_48kHz_161kbps_s_0_26.mp4 deleted file mode 100644 index 8b72c8437d3c..000000000000 Binary files a/media/tests/contents/media_api/videoeditor/MPEG4_SP_720x480_30fps_280kbps_AACLC_48kHz_161kbps_s_0_26.mp4 and /dev/null differ diff --git a/media/tests/contents/media_api/videoeditor/MPEG4_SP_720x480_30fps_280kbps_AACLC_48kHz_96kbps_s_0_21.mp4 b/media/tests/contents/media_api/videoeditor/MPEG4_SP_720x480_30fps_280kbps_AACLC_48kHz_96kbps_s_0_21.mp4 deleted file mode 100644 index 8752fc563640..000000000000 Binary files a/media/tests/contents/media_api/videoeditor/MPEG4_SP_720x480_30fps_280kbps_AACLC_48kHz_96kbps_s_0_21.mp4 and /dev/null differ diff --git a/media/tests/contents/media_api/videoeditor/MPEG4_SP_800x480_515kbps_15fps_AMR_NB_8KHz_12.2kbps_m_0_26.mp4 b/media/tests/contents/media_api/videoeditor/MPEG4_SP_800x480_515kbps_15fps_AMR_NB_8KHz_12.2kbps_m_0_26.mp4 deleted file mode 100644 index 829af3504286..000000000000 Binary files a/media/tests/contents/media_api/videoeditor/MPEG4_SP_800x480_515kbps_15fps_AMR_NB_8KHz_12.2kbps_m_0_26.mp4 and /dev/null differ diff --git a/media/tests/contents/media_api/videoeditor/MPEG4_SP_854x480_15fps_256kbps_AACLC_16khz_48kbps_s_0_26.mp4 b/media/tests/contents/media_api/videoeditor/MPEG4_SP_854x480_15fps_256kbps_AACLC_16khz_48kbps_s_0_26.mp4 deleted file mode 100644 index 8b60f433beb3..000000000000 Binary files a/media/tests/contents/media_api/videoeditor/MPEG4_SP_854x480_15fps_256kbps_AACLC_16khz_48kbps_s_0_26.mp4 and /dev/null differ diff --git a/media/tests/contents/media_api/videoeditor/Text_FileRenamedTo3gp.3gp b/media/tests/contents/media_api/videoeditor/Text_FileRenamedTo3gp.3gp deleted file mode 100644 index 02103c6d36e7..000000000000 --- a/media/tests/contents/media_api/videoeditor/Text_FileRenamedTo3gp.3gp +++ /dev/null @@ -1 +0,0 @@ -This is a text file \ No newline at end of file diff --git a/media/tests/contents/media_api/videoeditor/TransitionSpiral_QVGA.jpg b/media/tests/contents/media_api/videoeditor/TransitionSpiral_QVGA.jpg deleted file mode 100644 index 0863df9eb547..000000000000 Binary files a/media/tests/contents/media_api/videoeditor/TransitionSpiral_QVGA.jpg and /dev/null differ diff --git a/media/tests/contents/media_api/videoeditor/corrupted_H264_BP_640x480_12.5fps_256kbps_AACLC_16khz_24kbps_s_0_26.mp4 b/media/tests/contents/media_api/videoeditor/corrupted_H264_BP_640x480_12.5fps_256kbps_AACLC_16khz_24kbps_s_0_26.mp4 deleted file mode 100644 index 31627c7a8dd5..000000000000 Binary files a/media/tests/contents/media_api/videoeditor/corrupted_H264_BP_640x480_12.5fps_256kbps_AACLC_16khz_24kbps_s_0_26.mp4 and /dev/null differ diff --git a/packages/BackupRestoreConfirmation/AndroidManifest.xml b/packages/BackupRestoreConfirmation/AndroidManifest.xml index e67b3be43ea1..aede4590b627 100644 --- a/packages/BackupRestoreConfirmation/AndroidManifest.xml +++ b/packages/BackupRestoreConfirmation/AndroidManifest.xml @@ -21,6 +21,7 @@ + + volumes = mStorageManager.getVolumes(); + final List parallelUserIds = + ParallelSpaceManager.getInstance().getParallelUserIds(); + final List parallelUsers = + ParallelSpaceManager.getInstance().getParallelUsers(); + final int realUserId = UserHandle.myUserId(); + // Convert to parallel owner so that we are going to have same dick view + // across all parallel users. + final int userId = parallelUserIds.contains(realUserId) ? + ParallelSpaceManager.getInstance().getParallelOwnerId() : realUserId; for (VolumeInfo volume : volumes) { - if (!volume.isMountedReadable() || volume.getMountUserId() != userId) continue; + if (!volume.isMountedReadable()) continue; + int mountUserId = volume.getMountUserId(); + if (mountUserId != userId && !parallelUserIds.contains(mountUserId)) continue; final String rootId; final String title; @@ -177,7 +190,12 @@ private void updateVolumesLocked() { // We currently only support a single emulated volume per user mounted at // a time, and it's always considered the primary if (DEBUG) Log.d(TAG, "Found primary volume: " + volume); - rootId = ROOT_ID_PRIMARY_EMULATED; + + // Must be realUserId here otherwise it'll break SAF. + if (mountUserId == realUserId) + rootId = ROOT_ID_PRIMARY_EMULATED; + else + rootId = "parallel" + mountUserId; if (volume.isPrimaryEmulatedForUser(userId)) { // This is basically the user's primary device storage. @@ -192,6 +210,15 @@ private void updateVolumesLocked() { ? deviceName : getContext().getString(R.string.root_internal_storage); storageUuid = StorageManager.UUID_DEFAULT; + } else if (volume.isPrimaryEmulatedForUser(mountUserId)) { + String name = rootId; + for (UserInfo info : parallelUsers) { + if (info.id != mountUserId) continue; + if (info.name != null) + name = info.name; + } + title = name; + storageUuid = StorageManager.UUID_DEFAULT; } else { // This should cover all other storage devices, like an SD card // or USB OTG drive plugged in. Using getBestVolumeDescription() @@ -253,12 +280,12 @@ private void updateVolumesLocked() { if (volume.getType() == VolumeInfo.TYPE_PUBLIC) { root.flags |= Root.FLAG_HAS_SETTINGS; } - if (volume.isVisibleForUser(userId)) { - root.visiblePath = volume.getPathForUser(userId); + if (volume.isVisibleForUser(mountUserId)) { + root.visiblePath = volume.getPathForUser(mountUserId); } else { root.visiblePath = null; } - root.path = volume.getInternalPathForUser(userId); + root.path = volume.getInternalPathForUser(mountUserId); try { root.docId = getDocIdForFile(root.path); } catch (FileNotFoundException e) { diff --git a/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/google/CloudPrintPlugin.java b/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/google/CloudPrintPlugin.java index 93e6271319f6..3029d10d4cf3 100644 --- a/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/google/CloudPrintPlugin.java +++ b/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/google/CloudPrintPlugin.java @@ -55,18 +55,15 @@ public class CloudPrintPlugin implements PrintServicePlugin { private static final String PRIVET_SERVICE = "_privet._tcp"; /** The required mDNS service types */ - private static final Set PRINTER_SERVICE_TYPE = new HashSet() {{ - // Not checking _printer_._sub - add(PRIVET_SERVICE); - }}; + private static final Set PRINTER_SERVICE_TYPE = Set.of( + PRIVET_SERVICE); // Not checking _printer_._sub /** All possible connection states */ - private static final Set POSSIBLE_CONNECTION_STATES = new HashSet() {{ - add("online"); - add("offline"); - add("connecting"); - add("not-configured"); - }}; + private static final Set POSSIBLE_CONNECTION_STATES = Set.of( + "online", + "offline", + "connecting", + "not-configured"); private static final byte SUPPORTED_TXTVERS = '1'; diff --git a/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/mdnsFilter/MDNSFilterPlugin.java b/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/mdnsFilter/MDNSFilterPlugin.java index 34e7e3d1cd6b..0c5de2741d1b 100644 --- a/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/mdnsFilter/MDNSFilterPlugin.java +++ b/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/mdnsFilter/MDNSFilterPlugin.java @@ -37,9 +37,7 @@ public class MDNSFilterPlugin implements PrintServicePlugin { /** The mDNS service types supported */ - private static final Set PRINTER_SERVICE_TYPES = new HashSet() {{ - add("_ipp._tcp"); - }}; + private static final Set PRINTER_SERVICE_TYPES = Set.of("_ipp._tcp"); /** * The printer filter for {@link MDNSFilteredDiscovery} passing only mDNS results diff --git a/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/samsung/PrinterFilterMopria.java b/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/samsung/PrinterFilterMopria.java index d03bb1d76003..b9983c306289 100644 --- a/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/samsung/PrinterFilterMopria.java +++ b/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/samsung/PrinterFilterMopria.java @@ -23,7 +23,6 @@ import com.android.printservice.recommendation.util.MDNSFilteredDiscovery; import com.android.printservice.recommendation.util.MDNSUtils; -import java.util.HashSet; import java.util.Set; /** @@ -32,10 +31,7 @@ class PrinterFilterMopria implements MDNSFilteredDiscovery.PrinterFilter { private static final String TAG = "PrinterFilterMopria"; - static final Set MOPRIA_MDNS_SERVICES = new HashSet() {{ - add("_ipp._tcp"); - add("_ipps._tcp"); - }}; + static final Set MOPRIA_MDNS_SERVICES = Set.of("_ipp._tcp", "_ipps._tcp"); private static final String PDL__PDF = "application/pdf"; private static final String PDL__PCLM = "application/PCLm"; diff --git a/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/samsung/PrinterFilterSamsung.java b/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/samsung/PrinterFilterSamsung.java index b9b90988c37b..680dd84a25df 100644 --- a/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/samsung/PrinterFilterSamsung.java +++ b/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/samsung/PrinterFilterSamsung.java @@ -25,7 +25,6 @@ import com.android.printservice.recommendation.util.MDNSFilteredDiscovery; import com.android.printservice.recommendation.util.MDNSUtils; -import java.util.HashSet; import java.util.Locale; import java.util.Map; import java.util.Set; @@ -36,9 +35,7 @@ class PrinterFilterSamsung implements MDNSFilteredDiscovery.PrinterFilter { private static final String TAG = "PrinterFilterSamsung"; - static final Set SAMSUNG_MDNS_SERVICES = new HashSet() {{ - add("_pdl-datastream._tcp"); - }}; + static final Set SAMSUNG_MDNS_SERVICES = Set.of("_pdl-datastream._tcp"); private static final String[] NOT_SUPPORTED_MODELS = new String[]{ "SCX-5x15", @@ -57,9 +54,7 @@ class PrinterFilterSamsung implements MDNSFilteredDiscovery.PrinterFilter { private static final String ATTR_PRODUCT = "product"; private static final String ATTR_TY = "ty"; - private static Set SAMUNG_VENDOR_SET = new HashSet() {{ - add("samsung"); - }}; + private static final Set SAMUNG_VENDOR_SET = Set.of("samsung"); @Override public boolean matchesCriteria(NsdServiceInfo nsdServiceInfo) { diff --git a/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/samsung/SamsungRecommendationPlugin.java b/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/samsung/SamsungRecommendationPlugin.java index ae1bdcedaabb..cbd5833925b8 100644 --- a/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/samsung/SamsungRecommendationPlugin.java +++ b/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/samsung/SamsungRecommendationPlugin.java @@ -29,10 +29,11 @@ import java.util.Set; public class SamsungRecommendationPlugin implements PrintServicePlugin { - private static final Set ALL_MDNS_SERVICES = new HashSet() {{ - addAll(PrinterFilterMopria.MOPRIA_MDNS_SERVICES); - addAll(PrinterFilterSamsung.SAMSUNG_MDNS_SERVICES); - }}; + private static final Set ALL_MDNS_SERVICES = new HashSet(); + static { + ALL_MDNS_SERVICES.addAll(PrinterFilterMopria.MOPRIA_MDNS_SERVICES); + ALL_MDNS_SERVICES.addAll(PrinterFilterSamsung.SAMSUNG_MDNS_SERVICES); + } private final @NonNull Context mContext; private final @NonNull MDNSFilteredDiscovery mMDNSFilteredDiscovery; diff --git a/packages/PrintSpooler/res/values/styles.xml b/packages/PrintSpooler/res/values/styles.xml index 1e63a67e50a4..1fb899791cdb 100644 --- a/packages/PrintSpooler/res/values/styles.xml +++ b/packages/PrintSpooler/res/values/styles.xml @@ -28,7 +28,7 @@ 16dip 16dip ?android:attr/colorAccent - sans-serif-medium + @*android:string/config_bodyFontFamilyMedium 14sp - + \ No newline at end of file diff --git a/packages/PrintSpooler/src/com/android/printspooler/widget/PrintContentView.java b/packages/PrintSpooler/src/com/android/printspooler/widget/PrintContentView.java index 00b3736f8d6b..b0aa8f19f24b 100644 --- a/packages/PrintSpooler/src/com/android/printspooler/widget/PrintContentView.java +++ b/packages/PrintSpooler/src/com/android/printspooler/widget/PrintContentView.java @@ -402,7 +402,7 @@ public boolean tryCaptureView(View child, int pointerId) { @Override public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) { - if ((isOptionsClosed() || isOptionsClosed()) && dy <= 0) { + if (isOptionsClosed() && dy <= 0) { return; } diff --git a/packages/SettingsLib/ButtonPreference/res/values/attrs.xml b/packages/SettingsLib/ButtonPreference/res/values/attrs.xml index 9a4312aa8b36..944cffbb8793 100644 --- a/packages/SettingsLib/ButtonPreference/res/values/attrs.xml +++ b/packages/SettingsLib/ButtonPreference/res/values/attrs.xml @@ -19,4 +19,4 @@ - \ No newline at end of file + diff --git a/packages/SettingsLib/LayoutPreference/res/values/styles.xml b/packages/SettingsLib/LayoutPreference/res/values/styles.xml index 2ffe6d91651b..1b1f2eea0aa2 100644 --- a/packages/SettingsLib/LayoutPreference/res/values/styles.xml +++ b/packages/SettingsLib/LayoutPreference/res/values/styles.xml @@ -64,7 +64,7 @@ center 18sp ?android:attr/textColorPrimary - google-sans-medium + @*android:string/config_headlineFontFamilyMedium 8dp - \ No newline at end of file + diff --git a/packages/SettingsLib/SettingsTheme/res/values-v31/config.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/config.xml index 4860ad361744..bc2df3a44a6b 100644 --- a/packages/SettingsLib/SettingsTheme/res/values-v31/config.xml +++ b/packages/SettingsLib/SettingsTheme/res/values-v31/config.xml @@ -18,5 +18,5 @@ false false - - \ No newline at end of file + @*android:string/config_headlineFontFamily + diff --git a/packages/SettingsLib/res/drawable/ic_4g_plus_mobiledata.xml b/packages/SettingsLib/res/drawable/ic_4g_plus_mobiledata.xml index 1d048ae44049..1272ea7a30e1 100644 --- a/packages/SettingsLib/res/drawable/ic_4g_plus_mobiledata.xml +++ b/packages/SettingsLib/res/drawable/ic_4g_plus_mobiledata.xml @@ -14,20 +14,17 @@ limitations under the License. --> - - + android:width="27dp" + android:height="16dp" + android:viewportWidth="27.0" + android:viewportHeight="16.0"> + android:fillColor="#FF000000" + android:pathData="M2,10.98V9.81l4.28,-6.83h1.6v6.59h1.19v1.41H7.88V13H6.4v-2.02H2zM3.68,9.57H6.4V5.33H6.31L3.68,9.57z"/> + android:fillColor="#FF000000" + android:pathData="M15,13.22c-0.88,0 -1.66,-0.21 -2.34,-0.64c-0.67,-0.44 -1.2,-1.05 -1.6,-1.83c-0.38,-0.79 -0.57,-1.71 -0.57,-2.76c0,-1.05 0.21,-1.96 0.62,-2.74c0.41,-0.79 0.97,-1.4 1.67,-1.83c0.7,-0.44 1.5,-0.66 2.39,-0.66c1.09,0 2.01,0.25 2.74,0.76c0.75,0.5 1.22,1.21 1.41,2.11l-1.47,0.39c-0.17,-0.56 -0.48,-1 -0.94,-1.32c-0.45,-0.33 -1.03,-0.49 -1.75,-0.49c-0.57,0 -1.09,0.14 -1.57,0.43c-0.48,0.29 -0.85,0.71 -1.13,1.27c-0.28,0.56 -0.42,1.25 -0.42,2.07c0,0.81 0.14,1.5 0.41,2.07c0.28,0.56 0.65,0.98 1.11,1.27c0.47,0.29 0.98,0.43 1.54,0.43c0.57,0 1.06,-0.11 1.47,-0.34c0.42,-0.23 0.75,-0.55 0.99,-0.94c0.25,-0.4 0.41,-0.85 0.46,-1.36h-2.88V7.79h4.37c0,0.87 0,1.74 0,2.6c0,0.87 0,1.74 0,2.6h-1.41v-1.4h-0.08c-0.28,0.49 -0.67,0.88 -1.18,1.18C16.34,13.07 15.73,13.22 15,13.22z"/> + android:fillColor="#FF000000" + android:pathData="M24.62 5.24 24.62 2.89 23.32 2.89 23.32 5.24 20.97 5.24 20.97 6.54 23.32 6.54 23.32 8.89 24.62 8.89 24.62 6.54 26.97 6.54 26.97 5.24Z"/> diff --git a/packages/SettingsLib/res/drawable/ic_lte_plus_mobiledata.xml b/packages/SettingsLib/res/drawable/ic_lte_plus_mobiledata.xml index e5cdff33fe98..d1f4b6fd818b 100644 --- a/packages/SettingsLib/res/drawable/ic_lte_plus_mobiledata.xml +++ b/packages/SettingsLib/res/drawable/ic_lte_plus_mobiledata.xml @@ -14,23 +14,20 @@ limitations under the License. --> - - + android:width="31dp" + android:height="16dp" + android:viewportWidth="31.0" + android:viewportHeight="16.0"> + android:fillColor="#FF000000" + android:pathData="M2,13V2.98h1.53v8.57H8.3V13H2z"/> + android:fillColor="#FF000000" + android:pathData="M11.24,13V4.43H8.19V2.98h7.63v1.46h-3.05V13H11.24z"/> + android:fillColor="#FF000000" + android:pathData="M17.41,13V2.98h6.36v1.46h-4.83v2.65h4.4v1.46h-4.4v3.01h4.83V13H17.41z"/> + android:fillColor="#FF000000" + android:pathData="M28.72 5.24 28.72 2.89 27.42 2.89 27.42 5.24 25.07 5.24 25.07 6.54 27.42 6.54 27.42 8.89 28.72 8.89 28.72 6.54 31.07 6.54 31.07 5.24Z"/> diff --git a/packages/SettingsLib/res/drawable/ic_smartphone.xml b/packages/SettingsLib/res/drawable/ic_smartphone.xml index 09811bb4b792..62e2343e842e 100644 --- a/packages/SettingsLib/res/drawable/ic_smartphone.xml +++ b/packages/SettingsLib/res/drawable/ic_smartphone.xml @@ -22,7 +22,5 @@ android:tint="?android:attr/colorControlNormal"> + android:pathData="M17,1.01L7,1C5.9,1 5,1.9 5,3v18c0,1.1 0.9,2 2,2h10c1.1,0 2,-0.9 2,-2V3C19,1.9 18.1,1.01 17,1.01zM17,21H7l0,-1h10V21zM17,18H7V6h10V18zM17,4H7V3h10V4z"/> diff --git a/packages/SettingsLib/res/values/bootleg_strings.xml b/packages/SettingsLib/res/values/bootleg_strings.xml new file mode 100644 index 000000000000..342e7a6edbb4 --- /dev/null +++ b/packages/SettingsLib/res/values/bootleg_strings.xml @@ -0,0 +1,19 @@ + + + + + Smaller + + Smallest + diff --git a/packages/SettingsLib/res/values/dimens.xml b/packages/SettingsLib/res/values/dimens.xml index 226b119ebb76..d241ea2a10ae 100644 --- a/packages/SettingsLib/res/values/dimens.xml +++ b/packages/SettingsLib/res/values/dimens.xml @@ -113,7 +113,7 @@ 150% - 85% + 70% 18dp diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java index 6b9daa35f9ca..bb406626d8fd 100644 --- a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java +++ b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java @@ -127,6 +127,7 @@ static ApplicationsState getInstance(Application app, IPackageManager iPackageMa boolean mResumed; boolean mHaveDisabledApps; boolean mHaveInstantApps; + boolean mHaveHiddenApps; // Information about all applications. Synchronize on mEntriesMap // to protect access to these. @@ -210,7 +211,8 @@ private ApplicationsState(Application app, IPackageManager iPackageManager) { mAdminRetrieveFlags = PackageManager.MATCH_ANY_USER | PackageManager.MATCH_DISABLED_COMPONENTS | PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS; - mRetrieveFlags = PackageManager.MATCH_DISABLED_COMPONENTS | + mRetrieveFlags = PackageManager.MATCH_UNINSTALLED_PACKAGES | + PackageManager.MATCH_DISABLED_COMPONENTS | PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS; final List moduleInfos = mPm.getInstalledModules(0 /* flags */); @@ -297,6 +299,7 @@ void doResumeIfNeededLocked() { mHaveDisabledApps = false; mHaveInstantApps = false; + mHaveHiddenApps = false; for (int i = 0; i < mApplications.size(); i++) { final ApplicationInfo info = mApplications.get(i); // Need to trim out any applications that are disabled by @@ -316,6 +319,10 @@ void doResumeIfNeededLocked() { if (!mHaveInstantApps && AppUtils.isInstant(info)) { mHaveInstantApps = true; } + if (!mHaveHiddenApps && hasFlag(info.privateFlags, + ApplicationInfo.PRIVATE_FLAG_HIDDEN)) { + mHaveHiddenApps = true; + } int userId = UserHandle.getUserId(info.uid); final AppEntry entry = mEntriesMap.get(userId).get(info.packageName); @@ -389,14 +396,14 @@ private static boolean anyAppIsRemoved(List prevApplications, appPackages = new HashSet<>(); packageMap.put(userId, appPackages); } - if (hasFlag(application.flags, ApplicationInfo.FLAG_INSTALLED)) { + if (isInstalledOrHidden(application)) { appPackages.add(application.packageName); } } // detect any previous app is removed for (ApplicationInfo prevApplication : prevApplications) { - if (!hasFlag(prevApplication.flags, ApplicationInfo.FLAG_INSTALLED)) { + if (!isInstalledOrHidden(prevApplication)) { continue; } final String userId = String.valueOf(UserHandle.getUserId(prevApplication.uid)); @@ -426,6 +433,10 @@ public boolean haveInstantApps() { return mHaveInstantApps; } + public boolean haveHiddenApps() { + return mHaveHiddenApps; + } + boolean isHiddenModule(String packageName) { Boolean isHidden = mSystemModules.get(packageName); if (isHidden == null) { @@ -523,7 +534,7 @@ public void requestSize(String packageName, int userId) { if (DEBUG_LOCKING) Log.v(TAG, "requestSize about to acquire lock..."); synchronized (mEntriesMap) { AppEntry entry = mEntriesMap.get(userId).get(packageName); - if (entry != null && hasFlag(entry.info.flags, ApplicationInfo.FLAG_INSTALLED)) { + if (entry != null && isInstalledOrHidden(entry.info)) { mBackgroundHandler.post( () -> { try { @@ -615,6 +626,10 @@ void addPackage(String pkgName, int userId) { if (AppUtils.isInstant(info)) { mHaveInstantApps = true; } + if (!mHaveHiddenApps && hasFlag(info.privateFlags, + ApplicationInfo.PRIVATE_FLAG_HIDDEN)) { + mHaveHiddenApps = true; + } mApplications.add(info); if (!mBackgroundHandler.hasMessages(BackgroundHandler.MSG_LOAD_ENTRIES)) { mBackgroundHandler.sendEmptyMessage(BackgroundHandler.MSG_LOAD_ENTRIES); @@ -660,6 +675,16 @@ public void removePackage(String pkgName, int userId) { } } } + if (hasFlag(info.privateFlags, ApplicationInfo.PRIVATE_FLAG_HIDDEN)) { + mHaveHiddenApps = false; + for (ApplicationInfo otherInfo : mApplications) { + if (hasFlag(info.privateFlags, + ApplicationInfo.PRIVATE_FLAG_HIDDEN)) { + mHaveHiddenApps = true; + break; + } + } + } if (!mMainHandler.hasMessages(MainHandler.MSG_PACKAGE_LIST_CHANGED)) { mMainHandler.sendEmptyMessage(MainHandler.MSG_PACKAGE_LIST_CHANGED); } @@ -1153,24 +1178,25 @@ public void handleMessage(Message msg) { mMainHandler.sendMessage(m); } ApplicationInfo info = mApplications.get(i); - int userId = UserHandle.getUserId(info.uid); - if (mEntriesMap.get(userId).get(info.packageName) == null) { - numDone++; - getEntryLocked(info); - } - if (userId != 0 && mEntriesMap.indexOfKey(0) >= 0) { - // If this app is for a profile and we are on the owner, remove - // the owner entry if it isn't installed. This will prevent - // duplicates of work only apps showing up as 'not installed - // for this user'. - // Note: This depends on us traversing the users in order, which - // happens because of the way we generate the list in - // doResumeIfNeededLocked. - AppEntry entry = mEntriesMap.get(0).get(info.packageName); - if (entry != null && !hasFlag(entry.info.flags, - ApplicationInfo.FLAG_INSTALLED)) { - mEntriesMap.get(0).remove(info.packageName); - mAppEntries.remove(entry); + if (isInstalledOrHidden(info)) { + int userId = UserHandle.getUserId(info.uid); + if (mEntriesMap.get(userId).get(info.packageName) == null) { + numDone++; + getEntryLocked(info); + } + if (userId != 0 && mEntriesMap.indexOfKey(0) >= 0) { + // If this app is for a profile and we are on the owner, remove + // the owner entry if it isn't installed. This will prevent + // duplicates of work only apps showing up as 'not installed + // for this user'. + // Note: This depends on us traversing the users in order, which + // happens because of the way we generate the list in + // doResumeIfNeededLocked. + AppEntry entry = mEntriesMap.get(0).get(info.packageName); + if (entry != null && !isInstalledOrHidden(entry.info)) { + mEntriesMap.get(0).remove(info.packageName); + mAppEntries.remove(entry); + } } } } @@ -1230,6 +1256,7 @@ public void handleMessage(Message msg) { List intents = mPm.queryIntentActivitiesAsUser( launchIntent, PackageManager.MATCH_DISABLED_COMPONENTS + | PackageManager.MATCH_UNINSTALLED_PACKAGES | PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId @@ -1315,7 +1342,7 @@ public void handleMessage(Message msg) { long now = SystemClock.uptimeMillis(); for (int i = 0; i < mAppEntries.size(); i++) { AppEntry entry = mAppEntries.get(i); - if (hasFlag(entry.info.flags, ApplicationInfo.FLAG_INSTALLED) + if (isInstalledOrHidden(entry.info) && (entry.size == SIZE_UNKNOWN || entry.sizeStale)) { if (entry.sizeLoadStart == 0 || (entry.sizeLoadStart < (now - 20 * 1000))) { @@ -1502,6 +1529,10 @@ public void onReceive(Context context, Intent intent) { String pkgName = data.getEncodedSchemeSpecificPart(); for (int i = 0; i < mEntriesMap.size(); i++) { removePackage(pkgName, mEntriesMap.keyAt(i)); + if (!intent.getBooleanExtra(Intent.EXTRA_DATA_REMOVED, true) + && !intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) { + addPackage(pkgName, mEntriesMap.keyAt(i)); + } } } else if (Intent.ACTION_PACKAGE_CHANGED.equals(actionStr)) { Uri data = intent.getData(); @@ -1722,6 +1753,12 @@ private static boolean hasFlag(int flags, int flag) { return (flags & flag) != 0; } + private static boolean isInstalledOrHidden(ApplicationInfo info) { + return hasFlag(info.flags, ApplicationInfo.FLAG_INSTALLED) || + hasFlag(info.privateFlags, ApplicationInfo.PRIVATE_FLAG_HIDDEN); + + } + /** * Compare by label, then package name, then uid. */ @@ -1896,6 +1933,19 @@ public boolean filterApp(AppEntry entry) { } }; + public static final AppFilter FILTER_HIDDEN = new AppFilter() { + @Override + public void init() { + } + + @Override + public boolean filterApp(AppEntry entry) { + return !AppUtils.isInstant(entry.info) + && hasFlag(entry.info.privateFlags, + ApplicationInfo.PRIVATE_FLAG_HIDDEN); + } + }; + public static final AppFilter FILTER_INSTANT = new AppFilter() { @Override public void init() { diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothDiscoverableTimeoutReceiver.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothDiscoverableTimeoutReceiver.java index 6ce72bbc6909..3af64e2889e7 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothDiscoverableTimeoutReceiver.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothDiscoverableTimeoutReceiver.java @@ -82,4 +82,4 @@ public void onReceive(Context context, Intent intent) { Log.e(TAG, "localBluetoothAdapter is NULL!!"); } } -}; +} diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java index a9f4e9c74103..0345207fe312 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java @@ -28,7 +28,9 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.database.ContentObserver; import android.os.UserHandle; +import android.provider.Settings; import android.telephony.TelephonyManager; import android.util.Log; @@ -132,6 +134,19 @@ interface Handler { addHandler(BluetoothDevice.ACTION_ACL_DISCONNECTED, new AclStateChangedHandler()); registerAdapterIntentReceiver(); + + mContext.getContentResolver().registerContentObserver( + Settings.Global.getUriFor(Settings.Global.BLUETOOTH_OFF_TIMEOUT), + false, + new ContentObserver(handler) { + @Override + public void onChange(boolean selfChange) { + super.onChange(selfChange); + BluetoothTimeoutReceiver.setTimeoutAlarm(mContext, + Settings.Global.getLong(context.getContentResolver(), + Settings.Global.BLUETOOTH_OFF_TIMEOUT, 0)); + } + }); } /** Register to start receiving callbacks for Bluetooth events. */ @@ -279,6 +294,10 @@ public void onReceive(Context context, Intent intent, BluetoothDevice device) { } // Inform CachedDeviceManager that the adapter state has changed mDeviceManager.onBluetoothStateChanged(state); + if (state == BluetoothAdapter.STATE_ON) + BluetoothTimeoutReceiver.setTimeoutAlarm(context, + Settings.Global.getLong(context.getContentResolver(), + Settings.Global.BLUETOOTH_OFF_TIMEOUT, 0)); } } @@ -294,6 +313,9 @@ public void onReceive(Context context, Intent intent, BluetoothDevice device) { callback.onScanningStateChanged(mStarted); } mDeviceManager.onScanningStateChanged(mStarted); + BluetoothTimeoutReceiver.setTimeoutAlarm(context, + mStarted ? 0 : Settings.Global.getLong(context.getContentResolver(), + Settings.Global.BLUETOOTH_OFF_TIMEOUT, 0)); } } @@ -330,6 +352,11 @@ public void onReceive(Context context, Intent intent, BluetoothDevice device) { int state = intent.getIntExtra(BluetoothAdapter.EXTRA_CONNECTION_STATE, BluetoothAdapter.ERROR); dispatchConnectionStateChanged(cachedDevice, state); + if (state == BluetoothAdapter.STATE_DISCONNECTED) { + BluetoothTimeoutReceiver.setTimeoutAlarm(context, + Settings.Global.getLong(context.getContentResolver(), + Settings.Global.BLUETOOTH_OFF_TIMEOUT, 0)); + } } } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothTimeoutReceiver.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothTimeoutReceiver.java new file mode 100644 index 000000000000..bbeb5f35134f --- /dev/null +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothTimeoutReceiver.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2020 The Calyx Institute + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib.bluetooth; + +import android.app.AlarmManager; +import android.app.PendingIntent; +import android.bluetooth.BluetoothAdapter; +import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.util.Log; + +import java.util.Date; + +public class BluetoothTimeoutReceiver extends BroadcastReceiver { + private static final String TAG = "BluetoothTimeoutReceiver"; + + private static final String INTENT_TIMEOUT = "android.bluetooth.intent.TIMEOUT"; + + public static void setTimeoutAlarm(Context context, long alarmTime) { + Intent intent = new Intent(INTENT_TIMEOUT); + intent.setClassName("com.android.settings", "com.android.settingslib.bluetooth.BluetoothTimeoutReceiver"); + PendingIntent pending = PendingIntent.getBroadcast( + context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_MUTABLE); + AlarmManager alarmManager = + (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); + + if (alarmTime != 0) { + alarmTime = System.currentTimeMillis() + alarmTime; + Log.d(TAG, "setTimeoutAlarm(): alarmTime = " + new Date(alarmTime)); + alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, alarmTime, pending); + } else + alarmManager.cancel(pending); + } + + @Override + public void onReceive(Context context, Intent intent) { + BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); + if (intent.getAction() == null && !intent.getAction().equals(INTENT_TIMEOUT)) { + return; + } + if (bluetoothAdapter != null) { + if (bluetoothAdapter.getState() == BluetoothAdapter.STATE_ON && + bluetoothAdapter.getConnectionState() == BluetoothAdapter.STATE_DISCONNECTED) + bluetoothAdapter.disable(); + } else { + Log.e(TAG, "bluetoothAdapter is NULL!!"); + } + } +} diff --git a/packages/SettingsLib/src/com/android/settingslib/display/DisplayDensityUtils.java b/packages/SettingsLib/src/com/android/settingslib/display/DisplayDensityUtils.java index 44a37f4ccaca..27f7377c9e2f 100644 --- a/packages/SettingsLib/src/com/android/settingslib/display/DisplayDensityUtils.java +++ b/packages/SettingsLib/src/com/android/settingslib/display/DisplayDensityUtils.java @@ -50,7 +50,9 @@ public class DisplayDensityUtils { * largest. */ private static final int[] SUMMARIES_SMALLER = new int[] { - R.string.screen_zoom_summary_small + R.string.screen_zoom_summary_small, + R.string.screen_zoom_summary_smaller, + R.string.screen_zoom_summary_smallest }; /** diff --git a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryStatus.java b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryStatus.java index 132a631e25cc..693a2418067d 100644 --- a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryStatus.java +++ b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryStatus.java @@ -24,9 +24,13 @@ import static android.os.BatteryManager.EXTRA_LEVEL; import static android.os.BatteryManager.EXTRA_MAX_CHARGING_CURRENT; import static android.os.BatteryManager.EXTRA_MAX_CHARGING_VOLTAGE; +import static android.os.BatteryManager.EXTRA_TEMPERATURE; import static android.os.BatteryManager.EXTRA_PLUGGED; import static android.os.BatteryManager.EXTRA_PRESENT; import static android.os.BatteryManager.EXTRA_STATUS; +import static android.os.BatteryManager.EXTRA_DASH_CHARGER; +import static android.os.BatteryManager.EXTRA_WARP_CHARGER; +import static android.os.BatteryManager.EXTRA_VOOC_CHARGER; import android.content.Context; import android.content.Intent; @@ -45,22 +49,41 @@ public class BatteryStatus { public static final int CHARGING_SLOWLY = 0; public static final int CHARGING_REGULAR = 1; public static final int CHARGING_FAST = 2; + public static final int CHARGING_DASH = 3; + public static final int CHARGING_WARP = 4; + public static final int CHARGING_VOOC = 5; public final int status; public final int level; public final int plugged; public final int health; + public final int maxChargingCurrent; + public final int maxChargingVoltage; public final int maxChargingWattage; public final boolean present; + public final float temperature; + public final boolean dashChargeStatus; + public final boolean warpChargeStatus; + public final boolean voocChargeStatus; + public BatteryStatus(int status, int level, int plugged, int health, - int maxChargingWattage, boolean present) { + int maxChargingWattage, boolean present, + int maxChargingCurrent, int maxChargingVoltage, + float temperature, boolean dashChargeStatus, + boolean warpChargeStatus, boolean voocChargeStatus) { this.status = status; this.level = level; this.plugged = plugged; this.health = health; + this.maxChargingCurrent = maxChargingCurrent; + this.maxChargingVoltage = maxChargingVoltage; this.maxChargingWattage = maxChargingWattage; this.present = present; + this.temperature = temperature; + this.dashChargeStatus = dashChargeStatus; + this.warpChargeStatus = warpChargeStatus; + this.voocChargeStatus = voocChargeStatus; } public BatteryStatus(Intent batteryChangedIntent) { @@ -69,6 +92,10 @@ public BatteryStatus(Intent batteryChangedIntent) { level = batteryChangedIntent.getIntExtra(EXTRA_LEVEL, 0); health = batteryChangedIntent.getIntExtra(EXTRA_HEALTH, BATTERY_HEALTH_UNKNOWN); present = batteryChangedIntent.getBooleanExtra(EXTRA_PRESENT, true); + temperature = batteryChangedIntent.getIntExtra(EXTRA_TEMPERATURE, -1); + dashChargeStatus = batteryChangedIntent.getBooleanExtra(EXTRA_DASH_CHARGER, false); + warpChargeStatus = batteryChangedIntent.getBooleanExtra(EXTRA_WARP_CHARGER, false); + voocChargeStatus = batteryChangedIntent.getBooleanExtra(EXTRA_VOOC_CHARGER, false); final int maxChargingMicroAmp = batteryChangedIntent.getIntExtra(EXTRA_MAX_CHARGING_CURRENT, -1); @@ -82,8 +109,12 @@ public BatteryStatus(Intent batteryChangedIntent) { // to maintain precision equally on both factors. maxChargingWattage = (maxChargingMicroAmp / 1000) * (maxChargingMicroVolt / 1000); + maxChargingCurrent = maxChargingMicroAmp; + maxChargingVoltage = maxChargingMicroVolt; } else { maxChargingWattage = -1; + maxChargingCurrent = -1; + maxChargingVoltage = -1; } } @@ -166,7 +197,10 @@ public final int getChargingSpeed(Context context) { R.integer.config_chargingSlowlyThreshold); final int fastThreshold = context.getResources().getInteger( R.integer.config_chargingFastThreshold); - return maxChargingWattage <= 0 ? CHARGING_UNKNOWN : + return dashChargeStatus ? CHARGING_DASH : + warpChargeStatus ? CHARGING_WARP : + voocChargeStatus ? CHARGING_VOOC : + maxChargingWattage <= 0 ? CHARGING_UNKNOWN : maxChargingWattage < slowThreshold ? CHARGING_SLOWLY : maxChargingWattage > fastThreshold ? CHARGING_FAST : CHARGING_REGULAR; diff --git a/packages/SettingsLib/src/com/android/settingslib/mobile/MobileMappings.java b/packages/SettingsLib/src/com/android/settingslib/mobile/MobileMappings.java index c94195477cfc..b4e11ef368fd 100644 --- a/packages/SettingsLib/src/com/android/settingslib/mobile/MobileMappings.java +++ b/packages/SettingsLib/src/com/android/settingslib/mobile/MobileMappings.java @@ -18,6 +18,8 @@ import android.content.Context; import android.content.res.Resources; import android.os.PersistableBundle; +import android.os.UserHandle; +import android.provider.Settings; import android.telephony.Annotation; import android.telephony.CarrierConfigManager; import android.telephony.SubscriptionManager; @@ -230,6 +232,9 @@ public static Config readConfig(Context context) { res.getBoolean(com.android.internal.R.bool.config_alwaysUseCdmaRssi); config.hspaDataDistinguishable = res.getBoolean(R.bool.config_hspa_data_distinguishable); + config.show4gForLte = Settings.System.getIntForUser(context.getContentResolver(), + Settings.System.SHOW_FOURG_ICON, 0, + UserHandle.USER_CURRENT) == 1; CarrierConfigManager configMgr = (CarrierConfigManager) context.getSystemService(Context.CARRIER_CONFIG_SERVICE); @@ -240,8 +245,6 @@ public static Config readConfig(Context context) { if (b != null) { config.alwaysShowDataRatIcon = b.getBoolean( CarrierConfigManager.KEY_ALWAYS_SHOW_DATA_RAT_ICON_BOOL); - config.show4gForLte = b.getBoolean( - CarrierConfigManager.KEY_SHOW_4G_FOR_LTE_DATA_ICON_BOOL); config.show4glteForLte = b.getBoolean( CarrierConfigManager.KEY_SHOW_4GLTE_FOR_LTE_DATA_ICON_BOOL); config.show4gFor3g = b.getBoolean( diff --git a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java index 30c6645193c0..8e5355f4ad5c 100644 --- a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java +++ b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java @@ -27,6 +27,7 @@ import android.net.NetworkPolicy; import android.net.NetworkPolicyManager; import android.net.NetworkTemplate; +import android.net.wifi.WifiManager; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.text.format.DateUtils; @@ -40,6 +41,7 @@ import java.time.ZonedDateTime; import java.util.Iterator; import java.util.Locale; +import java.util.Set; public class DataUsageController { @@ -53,6 +55,7 @@ public class DataUsageController { private final Context mContext; private final NetworkPolicyManager mPolicyManager; private final NetworkStatsManager mNetworkStatsManager; + private final WifiManager mWifiManager; private Callback mCallback; private NetworkNameProvider mNetworkController; @@ -62,6 +65,7 @@ public DataUsageController(Context context) { mContext = context; mPolicyManager = NetworkPolicyManager.from(mContext); mNetworkStatsManager = context.getSystemService(NetworkStatsManager.class); + mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); mSubscriptionId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; } @@ -101,9 +105,34 @@ public DataUsageInfo getDataUsageInfo() { return getDataUsageInfo(template); } + public DataUsageInfo getDailyDataUsageInfo() { + NetworkTemplate template = DataUsageUtils.getMobileTemplate(mContext, mSubscriptionId); + + return getDailyDataUsageInfo(template); + } + public DataUsageInfo getWifiDataUsageInfo() { - NetworkTemplate template = new NetworkTemplate.Builder(NetworkTemplate.MATCH_WIFI).build(); - return getDataUsageInfo(template); + return getWifiDataUsageInfo(false); + } + + public DataUsageInfo getWifiDataUsageInfo(boolean currentNetwork) { + return getDataUsageInfo(getWifiNetworkTemplate(currentNetwork)); + } + + public DataUsageInfo getWifiDailyDataUsageInfo(boolean currentNetwork) { + return getDailyDataUsageInfo(getWifiNetworkTemplate(currentNetwork)); + } + + public NetworkTemplate getWifiNetworkTemplate(boolean currentNetwork) { + final NetworkTemplate.Builder builder = + new NetworkTemplate.Builder(NetworkTemplate.MATCH_WIFI); + if (currentNetwork) { + final String networkKey = mWifiManager.getConnectionInfo().getNetworkKey(); + if (networkKey != null) { + builder.setWifiNetworkKeys(Set.of(networkKey)); + } + } + return builder.build(); } public DataUsageInfo getDataUsageInfo(NetworkTemplate template) { @@ -143,6 +172,34 @@ public DataUsageInfo getDataUsageInfo(NetworkTemplate template) { return usage; } + public DataUsageInfo getDailyDataUsageInfo(NetworkTemplate template) { + final NetworkPolicy policy = findNetworkPolicy(template); + final long end = System.currentTimeMillis(); + long start = end - DataUsageUtils.getTodayMillis(); + + final long totalBytes = getUsageLevel(template, start, end); + if (totalBytes < 0L) { + return warn("no entry data"); + } + final DataUsageInfo usage = new DataUsageInfo(); + usage.startDate = start; + usage.usageLevel = totalBytes; + usage.period = formatDateRange(start, end); + usage.cycleStart = start; + usage.cycleEnd = end; + + if (policy != null) { + usage.limitLevel = policy.limitBytes > 0 ? policy.limitBytes : 0; + usage.warningLevel = policy.warningBytes > 0 ? policy.warningBytes : 0; + } else { + usage.warningLevel = getDefaultWarningLevel(); + } + if (usage != null && mNetworkController != null) { + usage.carrier = mNetworkController.getMobileDataNetworkName(); + } + return usage; + } + /** * Get the total usage level recorded in the network history * @param template the network template to retrieve the network history diff --git a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageUtils.java b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageUtils.java index 386a47ae29b0..bfb31e0a8a16 100644 --- a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageUtils.java +++ b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageUtils.java @@ -22,6 +22,7 @@ import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; +import android.text.format.Time; import android.util.Log; import com.android.internal.util.ArrayUtils; @@ -63,14 +64,14 @@ public static NetworkTemplate getMobileTemplate(Context context, int subId) { private static NetworkTemplate getNormalizedMobileTemplate( TelephonyManager telephonyManager, int subId) { final NetworkTemplate mobileTemplate = getMobileTemplateForSubId(telephonyManager, subId); - final Set mergedSubscriberIds = Set.of(telephonyManager - .createForSubscriptionId(subId).getMergedImsisFromGroup()); + final String[] mergedSubscriberIds = telephonyManager + .createForSubscriptionId(subId).getMergedImsisFromGroup(); if (ArrayUtils.isEmpty(mergedSubscriberIds)) { Log.i(TAG, "mergedSubscriberIds is null."); return mobileTemplate; } - return normalizeMobileTemplate(mobileTemplate, mergedSubscriberIds); + return normalizeMobileTemplate(mobileTemplate, Set.of(mergedSubscriberIds)); } private static NetworkTemplate normalizeMobileTemplate( @@ -104,4 +105,15 @@ private static NetworkTemplate getMobileTemplateForSubId( .setMeteredness(NetworkStats.METERED_YES) .build(); } + + /** + * Returns today's passed time in Millisecond + */ + public static long getTodayMillis() { + final long passedMillis; + Time time = new Time(); + time.set(System.currentTimeMillis()); + passedMillis = ((time.hour * 60 * 60) + (time.minute * 60) + time.second) * 1000; + return passedMillis; + } } diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java index 45c0d7823e99..1c446e551a08 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java @@ -17,6 +17,7 @@ import android.content.Context; import android.content.Intent; +import android.database.ContentObserver; import android.net.ConnectivityManager; import android.net.ConnectivityManager.NetworkCallback; import android.net.Network; @@ -203,6 +204,20 @@ public void networkCacheUpdated(List updatedNetworks) { mMainThreadHandler.post(() -> postResults()); } }; + + mContext.getContentResolver().registerContentObserver( + Settings.Global.getUriFor(Settings.Global.WIFI_OFF_TIMEOUT), + false, + new ContentObserver(mHandler) { + @Override + public void onChange(boolean selfChange) { + super.onChange(selfChange); + WifiTimeoutReceiver.setTimeoutAlarm(context, + Settings.Global.getLong(context.getContentResolver(), + Settings.Global.WIFI_OFF_TIMEOUT, 0)); + } + } + ); } public void setListening(boolean listening) { @@ -249,6 +264,10 @@ public void fetchInitialState() { updateRssi(mWifiInfo.getRssi()); maybeRequestNetworkScore(); } + } else { + WifiTimeoutReceiver.setTimeoutAlarm(mContext, + Settings.Global.getLong(mContext.getContentResolver(), + Settings.Global.WIFI_OFF_TIMEOUT, 0)); } updateStatusLabel(); } @@ -260,6 +279,11 @@ public void handleBroadcast(Intent intent) { String action = intent.getAction(); if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) { updateWifiState(); + if (intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, WifiManager.WIFI_STATE_UNKNOWN) == WifiManager.WIFI_STATE_ENABLED) { + WifiTimeoutReceiver.setTimeoutAlarm(mContext, + Settings.Global.getLong(mContext.getContentResolver(), + Settings.Global.WIFI_OFF_TIMEOUT, 0)); + } } } @@ -278,6 +302,10 @@ private void updateWifiInfo(WifiInfo wifiInfo) { subId = mWifiInfo.getSubscriptionId(); updateRssi(mWifiInfo.getRssi()); maybeRequestNetworkScore(); + } else { + WifiTimeoutReceiver.setTimeoutAlarm(mContext, + Settings.Global.getLong(mContext.getContentResolver(), + Settings.Global.WIFI_OFF_TIMEOUT, 0)); } } diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTimeoutReceiver.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTimeoutReceiver.java new file mode 100644 index 000000000000..1e7e663b24b3 --- /dev/null +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTimeoutReceiver.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2020 The Calyx Institute + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib.wifi; + +import android.app.AlarmManager; +import android.app.PendingIntent; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.net.wifi.WifiManager; +import android.util.Log; + +import java.util.Date; + +public class WifiTimeoutReceiver extends BroadcastReceiver { + private static final String TAG = "WifiTimeoutReceiver"; + + private static final String INTENT_TIMEOUT = "android.net.wifi.intent.TIMEOUT"; + + public static void setTimeoutAlarm(Context context, long alarmTime) { + Intent intent = new Intent(INTENT_TIMEOUT); + intent.setClass(context, WifiTimeoutReceiver.class); + PendingIntent pending = PendingIntent.getBroadcast( + context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_MUTABLE); + AlarmManager alarmManager = + (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); + + if (alarmTime != 0) { + alarmTime = System.currentTimeMillis() + alarmTime; + Log.d(TAG, "setTimeoutAlarm(): alarmTime = " + new Date(alarmTime)); + alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, alarmTime, pending); + } else + alarmManager.cancel(pending); + } + + @Override + public void onReceive(Context context, Intent intent) { + if (intent.getAction() == null && !intent.getAction().equals(INTENT_TIMEOUT)) { + return; + } + WifiManager wifiManager = context.getSystemService(WifiManager.class); + if (wifiManager != null) { + if (wifiManager.isWifiEnabled() && wifiManager.getCurrentNetwork() == null) + wifiManager.setWifiEnabled(false); + } else { + Log.e(TAG, "wifiManager is NULL!!"); + } + } +} diff --git a/packages/SettingsProvider/AndroidManifest.xml b/packages/SettingsProvider/AndroidManifest.xml index c5bea2ea6791..9288ac324992 100644 --- a/packages/SettingsProvider/AndroidManifest.xml +++ b/packages/SettingsProvider/AndroidManifest.xml @@ -4,6 +4,8 @@ android:sharedUserId="android.uid.system"> + + 100% true - true + false false false diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java index 453a71327b50..116da975b986 100644 --- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java +++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java @@ -215,6 +215,11 @@ public class SecureSettings { Settings.Secure.ASSIST_LONG_PRESS_HOME_ENABLED, Settings.Secure.BLUETOOTH_LE_BROADCAST_PROGRAM_INFO, Settings.Secure.BLUETOOTH_LE_BROADCAST_CODE, - Settings.Secure.BLUETOOTH_LE_BROADCAST_APP_SOURCE_NAME + Settings.Secure.BLUETOOTH_LE_BROADCAST_APP_SOURCE_NAME, + Settings.Secure.SCREEN_OFF_UDFPS_ENABLED, + Settings.Secure.LOCKSCREEN_PIN_SCRAMBLE_LAYOUT, + Settings.Secure.IGNORE_AUTH_CONFIRMATION, + Settings.Secure.GESTURE_NAVBAR_LENGTH_MODE, + Settings.Secure.SHOW_WIFI_STANDARD_ICON }; } diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java index a6bfc408be7e..1aee638057f4 100644 --- a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java +++ b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java @@ -89,5 +89,13 @@ public class SystemSettings { Settings.System.DISPLAY_COLOR_MODE, Settings.System.ALARM_ALERT, Settings.System.NOTIFICATION_LIGHT_PULSE, + Settings.System.USE_OLD_MOBILETYPE, + Settings.System.FULLSCREEN_GESTURES, + Settings.System.FLASHLIGHT_ON_CALL, + Settings.System.FLASHLIGHT_ON_CALL_IGNORE_DND, + Settings.System.FLASHLIGHT_ON_CALL_RATE, + Settings.System.OMNIJAWS_WEATHER_ICON_PACK, + Settings.System.OMNI_LOCKSCREEN_WEATHER_ENABLED, + Settings.System.AICP_LOCKSCREEN_WEATHER_STYLE, }; } diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java index a39735ffe2c7..d7905d8c44cd 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java @@ -347,5 +347,12 @@ public class SecureSettingsValidators { VALIDATORS.put(Secure.BLUETOOTH_LE_BROADCAST_PROGRAM_INFO, ANY_STRING_VALIDATOR); VALIDATORS.put(Secure.BLUETOOTH_LE_BROADCAST_CODE, ANY_STRING_VALIDATOR); VALIDATORS.put(Secure.BLUETOOTH_LE_BROADCAST_APP_SOURCE_NAME, ANY_STRING_VALIDATOR); + VALIDATORS.put(Secure.SCREEN_OFF_UDFPS_ENABLED, BOOLEAN_VALIDATOR); + VALIDATORS.put(Secure.LOCKSCREEN_PIN_SCRAMBLE_LAYOUT, BOOLEAN_VALIDATOR); + VALIDATORS.put(Secure.FACE_UNLOCK_METHOD, BOOLEAN_VALIDATOR); + VALIDATORS.put(Secure.IGNORE_AUTH_CONFIRMATION, BOOLEAN_VALIDATOR); + VALIDATORS.put(Secure.GESTURE_NAVBAR_LENGTH_MODE, new InclusiveIntegerRangeValidator(0, 2)); + VALIDATORS.put(Secure.VOLUME_LINK_NOTIFICATION, BOOLEAN_VALIDATOR); + VALIDATORS.put(Secure.SHOW_WIFI_STANDARD_ICON, BOOLEAN_VALIDATOR); } } diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java index 06712cc68b89..401e431668af 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java @@ -207,5 +207,13 @@ public boolean validate(@Nullable String value) { VALIDATORS.put(System.WIFI_STATIC_DNS2, LENIENT_IP_ADDRESS_VALIDATOR); VALIDATORS.put(System.SHOW_BATTERY_PERCENT, BOOLEAN_VALIDATOR); VALIDATORS.put(System.NOTIFICATION_LIGHT_PULSE, BOOLEAN_VALIDATOR); + VALIDATORS.put(System.USE_OLD_MOBILETYPE, BOOLEAN_VALIDATOR); + VALIDATORS.put(System.FULLSCREEN_GESTURES, BOOLEAN_VALIDATOR); + VALIDATORS.put(System.FLASHLIGHT_ON_CALL, new InclusiveIntegerRangeValidator(0, 4)); + VALIDATORS.put(System.FLASHLIGHT_ON_CALL_IGNORE_DND, BOOLEAN_VALIDATOR); + VALIDATORS.put(System.FLASHLIGHT_ON_CALL_RATE, new InclusiveIntegerRangeValidator(1, 5)); + VALIDATORS.put(System.OMNIJAWS_WEATHER_ICON_PACK, ANY_STRING_VALIDATOR); + VALIDATORS.put(System.OMNI_LOCKSCREEN_WEATHER_ENABLED, BOOLEAN_VALIDATOR); + VALIDATORS.put(System.AICP_LOCKSCREEN_WEATHER_STYLE, BOOLEAN_VALIDATOR); } } diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java index b851232ace82..40b8633411f8 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java @@ -2660,7 +2660,8 @@ private String getOldDefaultDeviceName() { } private String getDefaultDeviceName() { - return mContext.getResources().getString(R.string.def_device_name_simple, Build.MODEL); + return mContext.getResources().getString(R.string.def_device_name_simple, + SystemProperties.get("ro.product.marketname", Build.MODEL)); } private TelephonyManager getTelephonyManager() { diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java index 808ea9ede9dc..a816d5dc0de5 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java @@ -173,6 +173,7 @@ public void restoreValue(Context context, ContentResolver cr, ContentValues cont } else if (isAlreadyConfiguredCriticalAccessibilitySetting(name)) { return; } else if (Settings.System.RINGTONE.equals(name) + || Settings.System.RINGTONE2.equals(name) || Settings.System.NOTIFICATION_SOUND.equals(name) || Settings.System.ALARM_ALERT.equals(name)) { setRingtone(name, value); @@ -248,11 +249,12 @@ private boolean shouldSkipAutoRotateRestore() { public String onBackupValue(String name, String value) { // Special processing for backing up ringtones & notification sounds - if (Settings.System.RINGTONE.equals(name) + if (Settings.System.RINGTONE.equals(name) || Settings.System.RINGTONE2.equals(name) || Settings.System.NOTIFICATION_SOUND.equals(name) || Settings.System.ALARM_ALERT.equals(name)) { if (value == null) { - if (Settings.System.RINGTONE.equals(name)) { + if (Settings.System.RINGTONE.equals(name) + || Settings.System.RINGTONE2.equals(name)) { // For ringtones, we need to distinguish between non-telephony vs telephony if (mTelephonyManager != null && mTelephonyManager.isVoiceCapable()) { // Backup a null ringtone as silent on voice-capable devices @@ -302,7 +304,7 @@ public boolean isReplacedSystemSetting(String setting) { /** * Sets the ringtone of type specified by the name. * - * @param name should be Settings.System.RINGTONE, Settings.System.NOTIFICATION_SOUND + * @param name should be Settings.System.RINGTONE, Settings.System.RINGTONE2, Settings.System.NOTIFICATION_SOUND * or Settings.System.ALARM_ALERT. * @param value can be a canonicalized uri or "_silent" to indicate a silent (null) ringtone. */ @@ -329,6 +331,8 @@ private int getRingtoneType(String name) { switch (name) { case Settings.System.RINGTONE: return RingtoneManager.TYPE_RINGTONE; + case Settings.System.RINGTONE2: + return RingtoneManager.TYPE_RINGTONE; case Settings.System.NOTIFICATION_SOUND: return RingtoneManager.TYPE_NOTIFICATION; case Settings.System.ALARM_ALERT: diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java index aa3a983e9971..ecc2e8e50925 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java @@ -2807,9 +2807,15 @@ private static void dumpProtoSystemSettingsLocked( dumpSetting(s, p, Settings.System.RINGTONE, SystemSettingsProto.Ringtone.DEFAULT_URI); + dumpSetting(s, p, + Settings.System.RINGTONE2, + SystemSettingsProto.Ringtone.DEFAULT_URI); dumpSetting(s, p, Settings.System.RINGTONE_CACHE, SystemSettingsProto.Ringtone.CACHE); + dumpSetting(s, p, + Settings.System.RINGTONE2_CACHE, + SystemSettingsProto.Ringtone.CACHE); p.end(ringtoneToken); final long rotationToken = p.start(SystemSettingsProto.ROTATION); diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index a6edb0f0e2e3..a7695a780286 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -832,6 +832,9 @@ public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundEx if (Settings.System.RINGTONE_CACHE_URI.equals(uri)) { cacheRingtoneSetting = Settings.System.RINGTONE; cacheName = Settings.System.RINGTONE_CACHE; + } else if (Settings.System.RINGTONE2_CACHE_URI.equals(uri)) { + cacheRingtoneSetting = Settings.System.RINGTONE2; + cacheName = Settings.System.RINGTONE2_CACHE; } else if (Settings.System.NOTIFICATION_SOUND_CACHE_URI.equals(uri)) { cacheRingtoneSetting = Settings.System.NOTIFICATION_SOUND; cacheName = Settings.System.NOTIFICATION_SOUND_CACHE; @@ -1922,6 +1925,8 @@ private boolean mutateSystemSetting(String name, String value, int runAsUserId, String cacheName = null; if (Settings.System.RINGTONE.equals(name)) { cacheName = Settings.System.RINGTONE_CACHE; + } else if (Settings.System.RINGTONE2.equals(name)) { + cacheName = Settings.System.RINGTONE2_CACHE; } else if (Settings.System.NOTIFICATION_SOUND.equals(name)) { cacheName = Settings.System.NOTIFICATION_SOUND_CACHE; } else if (Settings.System.ALARM_ALERT.equals(name)) { diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java index fd7554f11873..cd667ca05f61 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java @@ -48,6 +48,7 @@ import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; import com.android.internal.util.FrameworkStatsLog; @@ -376,9 +377,11 @@ public void resetSettingDefaultValueLocked(String name) { Setting newSetting = new Setting(name, oldSetting.getValue(), null, oldSetting.getPackageName(), oldSetting.getTag(), false, oldSetting.getId()); + int newSize = getNewMemoryUsagePerPackageLocked(newSetting.getPackageName(), 0, + oldValue, newSetting.getValue(), oldDefaultValue, newSetting.getDefaultValue()); + checkNewMemoryUsagePerPackageLocked(newSetting.getPackageName(), newSize); mSettings.put(name, newSetting); - updateMemoryUsagePerPackageLocked(newSetting.getPackageName(), oldValue, - newSetting.getValue(), oldDefaultValue, newSetting.getDefaultValue()); + updateMemoryUsagePerPackageLocked(newSetting.getPackageName(), newSize); scheduleWriteIfNeededLocked(); } } @@ -410,6 +413,13 @@ public boolean insertSettingLocked(String name, String value, String tag, Setting oldState = mSettings.get(name); String oldValue = (oldState != null) ? oldState.value : null; String oldDefaultValue = (oldState != null) ? oldState.defaultValue : null; + String newDefaultValue = makeDefault ? value : oldDefaultValue; + + int newSize = getNewMemoryUsagePerPackageLocked(packageName, + oldValue == null ? name.length() : 0 /* deltaKeySize */, + oldValue, value, oldDefaultValue, newDefaultValue); + checkNewMemoryUsagePerPackageLocked(packageName, newSize); + Setting newState; if (oldState != null) { @@ -430,8 +440,7 @@ oldValue, tag, makeDefault, getUserIdFromKey(mKey), addHistoricalOperationLocked(HISTORICAL_OPERATION_UPDATE, newState); - updateMemoryUsagePerPackageLocked(packageName, oldValue, value, - oldDefaultValue, newState.getDefaultValue()); + updateMemoryUsagePerPackageLocked(packageName, newSize); scheduleWriteIfNeededLocked(); @@ -552,13 +561,18 @@ public boolean deleteSettingLocked(String name) { } Setting oldState = mSettings.remove(name); + if (oldState == null) { + return false; + } + int newSize = getNewMemoryUsagePerPackageLocked(oldState.packageName, + -name.length() /* deltaKeySize */, + oldState.value, null, oldState.defaultValue, null); FrameworkStatsLog.write(FrameworkStatsLog.SETTING_CHANGED, name, /* value= */ "", /* newValue= */ "", oldState.value, /* tag */ "", false, getUserIdFromKey(mKey), FrameworkStatsLog.SETTING_CHANGED__REASON__DELETED); - updateMemoryUsagePerPackageLocked(oldState.packageName, oldState.value, - null, oldState.defaultValue, null); + updateMemoryUsagePerPackageLocked(oldState.packageName, newSize); addHistoricalOperationLocked(HISTORICAL_OPERATION_DELETE, oldState); @@ -575,20 +589,23 @@ public boolean resetSettingLocked(String name) { } Setting setting = mSettings.get(name); + if (setting == null) { + return false; + } Setting oldSetting = new Setting(setting); String oldValue = setting.getValue(); String oldDefaultValue = setting.getDefaultValue(); + int newSize = getNewMemoryUsagePerPackageLocked(setting.packageName, 0, oldValue, + oldDefaultValue, oldDefaultValue, oldDefaultValue); + checkNewMemoryUsagePerPackageLocked(setting.packageName, newSize); + if (!setting.reset()) { return false; } - String newValue = setting.getValue(); - String newDefaultValue = setting.getDefaultValue(); - - updateMemoryUsagePerPackageLocked(setting.packageName, oldValue, - newValue, oldDefaultValue, newDefaultValue); + updateMemoryUsagePerPackageLocked(setting.packageName, newSize); addHistoricalOperationLocked(HISTORICAL_OPERATION_RESET, oldSetting); @@ -696,38 +713,49 @@ public void dumpHistoricalOperations(PrintWriter pw) { } @GuardedBy("mLock") - private void updateMemoryUsagePerPackageLocked(String packageName, String oldValue, - String newValue, String oldDefaultValue, String newDefaultValue) { - if (mMaxBytesPerAppPackage == MAX_BYTES_PER_APP_PACKAGE_UNLIMITED) { - return; - } + private boolean isExemptFromMemoryUsageCap(String packageName) { + return mMaxBytesPerAppPackage == MAX_BYTES_PER_APP_PACKAGE_UNLIMITED + || SYSTEM_PACKAGE_NAME.equals(packageName); + } - if (SYSTEM_PACKAGE_NAME.equals(packageName)) { + @GuardedBy("mLock") + private void checkNewMemoryUsagePerPackageLocked(String packageName, int newSize) + throws IllegalStateException { + if (isExemptFromMemoryUsageCap(packageName)) { return; } + if (newSize > mMaxBytesPerAppPackage) { + throw new IllegalStateException("You are adding too many system settings. " + + "You should stop using system settings for app specific data" + + " package: " + packageName); + } + } + @GuardedBy("mLock") + private int getNewMemoryUsagePerPackageLocked(String packageName, int deltaKeySize, + String oldValue, String newValue, String oldDefaultValue, String newDefaultValue) { + if (isExemptFromMemoryUsageCap(packageName)) { + return 0; + } + final Integer currentSize = mPackageToMemoryUsage.get(packageName); final int oldValueSize = (oldValue != null) ? oldValue.length() : 0; final int newValueSize = (newValue != null) ? newValue.length() : 0; final int oldDefaultValueSize = (oldDefaultValue != null) ? oldDefaultValue.length() : 0; final int newDefaultValueSize = (newDefaultValue != null) ? newDefaultValue.length() : 0; - final int deltaSize = newValueSize + newDefaultValueSize + final int deltaSize = deltaKeySize + newValueSize + newDefaultValueSize - oldValueSize - oldDefaultValueSize; + return Math.max((currentSize != null) ? currentSize + deltaSize : deltaSize, 0); + } - Integer currentSize = mPackageToMemoryUsage.get(packageName); - final int newSize = Math.max((currentSize != null) - ? currentSize + deltaSize : deltaSize, 0); - - if (newSize > mMaxBytesPerAppPackage) { - throw new IllegalStateException("You are adding too many system settings. " - + "You should stop using system settings for app specific data" - + " package: " + packageName); + @GuardedBy("mLock") + private void updateMemoryUsagePerPackageLocked(String packageName, int newSize) { + if (isExemptFromMemoryUsageCap(packageName)) { + return; } - if (DEBUG) { Slog.i(LOG_TAG, "Settings for package: " + packageName + " size: " + newSize + " bytes."); } - mPackageToMemoryUsage.put(packageName, newSize); } @@ -1556,4 +1584,11 @@ private static boolean isSystemPackage(@Nullable ApplicationInfo aInfo) { } return false; } + + @VisibleForTesting + public int getMemoryUsage(String packageName) { + synchronized (mLock) { + return mPackageToMemoryUsage.getOrDefault(packageName, 0); + } + } } diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java index 69eb7133f46f..f6d43292eafe 100644 --- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java +++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java @@ -20,6 +20,8 @@ import android.util.TypedXmlSerializer; import android.util.Xml; +import com.google.common.base.Strings; + import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileOutputStream; @@ -276,4 +278,132 @@ private SettingsState getSettingStateObject() { settingsState.setVersionLocked(SettingsState.SETTINGS_VERSION_NEW_ENCODING); return settingsState; } + + public void testInsertSetting_memoryUsage() { + SettingsState settingsState = new SettingsState(getContext(), mLock, mSettingsFile, 1, + SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper()); + // No exception should be thrown when there is no cap + settingsState.insertSettingLocked(SETTING_NAME, Strings.repeat("A", 20001), + null, false, "p1"); + settingsState.deleteSettingLocked(SETTING_NAME); + + settingsState = new SettingsState(getContext(), mLock, mSettingsFile, 1, + SettingsState.MAX_BYTES_PER_APP_PACKAGE_LIMITED, Looper.getMainLooper()); + // System package doesn't have memory usage limit + settingsState.insertSettingLocked(SETTING_NAME, Strings.repeat("A", 20001), + null, false, SYSTEM_PACKAGE); + settingsState.deleteSettingLocked(SETTING_NAME); + + // Should not throw if usage is under the cap + settingsState.insertSettingLocked(SETTING_NAME, Strings.repeat("A", 19975), + null, false, "p1"); + settingsState.deleteSettingLocked(SETTING_NAME); + try { + settingsState.insertSettingLocked(SETTING_NAME, Strings.repeat("A", 20001), + null, false, "p1"); + fail("Should throw because it exceeded per package memory usage"); + } catch (IllegalStateException ex) { + assertTrue(ex.getMessage().contains("p1")); + } + try { + settingsState.insertSettingLocked(SETTING_NAME, Strings.repeat("A", 20001), + null, false, "p1"); + fail("Should throw because it exceeded per package memory usage"); + } catch (IllegalStateException ex) { + assertTrue(ex.getMessage().contains("p1")); + } + assertTrue(settingsState.getSettingLocked(SETTING_NAME).isNull()); + try { + settingsState.insertSettingLocked(Strings.repeat("A", 20001), "", + null, false, "p1"); + fail("Should throw because it exceeded per package memory usage"); + } catch (IllegalStateException ex) { + assertTrue(ex.getMessage().contains("You are adding too many system settings")); + } + } + + public void testMemoryUsagePerPackage() { + SettingsState settingsState = new SettingsState(getContext(), mLock, mSettingsFile, 1, + SettingsState.MAX_BYTES_PER_APP_PACKAGE_LIMITED, Looper.getMainLooper()); + + // Test inserting one key with default + final String testKey1 = SETTING_NAME; + final String testValue1 = Strings.repeat("A", 100); + settingsState.insertSettingLocked(testKey1, testValue1, null, true, TEST_PACKAGE); + int expectedMemUsage = testKey1.length() + testValue1.length() + + testValue1.length() /* size for default */; + assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE)); + + // Test inserting another key + final String testKey2 = SETTING_NAME + "2"; + settingsState.insertSettingLocked(testKey2, testValue1, null, false, TEST_PACKAGE); + expectedMemUsage += testKey2.length() + testValue1.length(); + assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE)); + + // Test updating first key with new default + final String testValue2 = Strings.repeat("A", 300); + settingsState.insertSettingLocked(testKey1, testValue2, null, true, TEST_PACKAGE); + expectedMemUsage += (testValue2.length() - testValue1.length()) * 2; + assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE)); + + // Test updating first key without new default + final String testValue3 = Strings.repeat("A", 50); + settingsState.insertSettingLocked(testKey1, testValue3, null, false, TEST_PACKAGE); + expectedMemUsage -= testValue2.length() - testValue3.length(); + assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE)); + + // Test updating second key + settingsState.insertSettingLocked(testKey2, testValue2, null, false, TEST_PACKAGE); + expectedMemUsage -= testValue1.length() - testValue2.length(); + assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE)); + + // Test resetting key + settingsState.resetSettingLocked(testKey1); + expectedMemUsage += testValue2.length() - testValue3.length(); + assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE)); + + // Test resetting default value + settingsState.resetSettingDefaultValueLocked(testKey1); + expectedMemUsage -= testValue2.length(); + assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE)); + + // Test deletion + settingsState.deleteSettingLocked(testKey2); + expectedMemUsage -= testValue2.length() + testKey2.length() /* key is deleted too */; + assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE)); + + // Test another package with a different key + final String testPackage2 = TEST_PACKAGE + "2"; + final String testKey3 = SETTING_NAME + "3"; + settingsState.insertSettingLocked(testKey3, testValue1, null, true, testPackage2); + assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE)); + final int expectedMemUsage2 = testKey3.length() + testValue1.length() * 2; + assertEquals(expectedMemUsage2, settingsState.getMemoryUsage(testPackage2)); + + // Test system package + settingsState.insertSettingLocked(testKey1, testValue1, null, true, SYSTEM_PACKAGE); + assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE)); + assertEquals(expectedMemUsage2, settingsState.getMemoryUsage(testPackage2)); + assertEquals(0, settingsState.getMemoryUsage(SYSTEM_PACKAGE)); + + // Test invalid value + try { + settingsState.insertSettingLocked(testKey1, Strings.repeat("A", 20001), null, false, + TEST_PACKAGE); + fail("Should throw because it exceeded per package memory usage"); + } catch (IllegalStateException ex) { + assertTrue(ex.getMessage().contains("You are adding too many system settings")); + } + assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE)); + + // Test invalid key + try { + settingsState.insertSettingLocked(Strings.repeat("A", 20001), "", null, false, + TEST_PACKAGE); + fail("Should throw because it exceeded per package memory usage"); + } catch (IllegalStateException ex) { + assertTrue(ex.getMessage().contains("You are adding too many system settings")); + } + assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE)); + } } diff --git a/packages/StatementService/src/com/android/statementservice/domain/DomainVerificationReceiverV1.kt b/packages/StatementService/src/com/android/statementservice/domain/DomainVerificationReceiverV1.kt index 0ec8ed3416b8..acb54f6093de 100644 --- a/packages/StatementService/src/com/android/statementservice/domain/DomainVerificationReceiverV1.kt +++ b/packages/StatementService/src/com/android/statementservice/domain/DomainVerificationReceiverV1.kt @@ -67,6 +67,10 @@ class DomainVerificationReceiverV1 : BaseDomainVerificationReceiver() { } } + //clear sp before enqueue unique work since policy is REPLACE + val deContext = context.createDeviceProtectedStorageContext() + val editor = deContext?.getSharedPreferences(packageName, Context.MODE_PRIVATE)?.edit() + editor?.clear()?.apply() WorkManager.getInstance(context) .beginUniqueWork( "$PACKAGE_WORK_PREFIX_V1$packageName", diff --git a/packages/StatementService/src/com/android/statementservice/domain/worker/CollectV1Worker.kt b/packages/StatementService/src/com/android/statementservice/domain/worker/CollectV1Worker.kt index 3a3aea9288cd..36c81722b5d9 100644 --- a/packages/StatementService/src/com/android/statementservice/domain/worker/CollectV1Worker.kt +++ b/packages/StatementService/src/com/android/statementservice/domain/worker/CollectV1Worker.kt @@ -41,9 +41,7 @@ class CollectV1Worker(appContext: Context, params: WorkerParameters) : Data.Builder() .putInt(VERIFICATION_ID_KEY, verificationId) .apply { - if (DEBUG) { - putString(PACKAGE_NAME_KEY, packageName) - } + putString(PACKAGE_NAME_KEY, packageName) } .build() ) @@ -52,6 +50,18 @@ class CollectV1Worker(appContext: Context, params: WorkerParameters) : override suspend fun doWork() = coroutineScope { if (!AndroidUtils.isReceiverV1Enabled(appContext)) { + //clear sp and commit here + val inputData = params.inputData + val packageName = inputData.getString(PACKAGE_NAME_KEY) + val deContext = appContext.createDeviceProtectedStorageContext() + val sp = deContext?.getSharedPreferences(packageName, Context.MODE_PRIVATE) + val editor = sp?.edit() + editor?.clear()?.commit() + //delete sp file + val retOfDel = deContext?.deleteSharedPreferences(packageName) + if (DEBUG) { + Log.d(TAG, "delete sp for $packageName return $retOfDel") + } return@coroutineScope Result.success() } @@ -59,7 +69,10 @@ class CollectV1Worker(appContext: Context, params: WorkerParameters) : val verificationId = inputData.getInt(VERIFICATION_ID_KEY, -1) val successfulHosts = mutableListOf() val failedHosts = mutableListOf() - inputData.keyValueMap.entries.forEach { (key, _) -> + val packageName = inputData.getString(PACKAGE_NAME_KEY) + val deContext = appContext.createDeviceProtectedStorageContext() + val sp = deContext?.getSharedPreferences(packageName, Context.MODE_PRIVATE) + sp?.all?.entries?.forEach { (key, _) -> when { key.startsWith(SingleV1RequestWorker.HOST_SUCCESS_PREFIX) -> successfulHosts += key.removePrefix(SingleV1RequestWorker.HOST_SUCCESS_PREFIX) @@ -69,7 +82,6 @@ class CollectV1Worker(appContext: Context, params: WorkerParameters) : } if (DEBUG) { - val packageName = inputData.getString(PACKAGE_NAME_KEY) Log.d( TAG, "Domain verification v1 request for $packageName: " + "success = $successfulHosts, failed = $failedHosts" @@ -84,6 +96,15 @@ class CollectV1Worker(appContext: Context, params: WorkerParameters) : appContext.packageManager.verifyIntentFilter(verificationId, resultCode, failedHosts) + //clear sp and commit here + val editor = sp?.edit() + editor?.clear()?.commit() + //delete sp file + val retOfDel = deContext?.deleteSharedPreferences(packageName) + if (DEBUG) { + Log.d(TAG, "delete sp for $packageName return $retOfDel") + } + Result.success() } } diff --git a/packages/StatementService/src/com/android/statementservice/domain/worker/SingleV1RequestWorker.kt b/packages/StatementService/src/com/android/statementservice/domain/worker/SingleV1RequestWorker.kt index cd8a18218004..7a198cb59ca4 100644 --- a/packages/StatementService/src/com/android/statementservice/domain/worker/SingleV1RequestWorker.kt +++ b/packages/StatementService/src/com/android/statementservice/domain/worker/SingleV1RequestWorker.kt @@ -71,16 +71,18 @@ class SingleV1RequestWorker(appContext: Context, params: WorkerParameters) : // Coerce failure results into success so that final collection task gets a chance to run when (result) { - is Result.Success -> Result.success( - Data.Builder() - .putInt("$HOST_SUCCESS_PREFIX$host", status.value) - .build() - ) - is Result.Failure -> Result.success( - Data.Builder() - .putInt("$HOST_FAILURE_PREFIX$host", status.value) - .build() - ) + is Result.Success -> { + val deContext = appContext.createDeviceProtectedStorageContext() + val sp = deContext?.getSharedPreferences(packageName, Context.MODE_PRIVATE) + sp?.edit()?.putInt("$HOST_SUCCESS_PREFIX$host", status.value)?.apply() + Result.success() + } + is Result.Failure -> { + val deContext = appContext.createDeviceProtectedStorageContext() + val sp = deContext?.getSharedPreferences(packageName, Context.MODE_PRIVATE) + sp?.edit()?.putInt("$HOST_FAILURE_PREFIX$host", status.value)?.apply() + Result.success() + } else -> result } } diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp index df6f08df7c56..7e2f6e3d85a8 100644 --- a/packages/SystemUI/Android.bp +++ b/packages/SystemUI/Android.bp @@ -125,9 +125,14 @@ android_library { "jsr330", "lottie", "LowLightDreamLib", + "faceunlock_framework", ], manifest: "AndroidManifest.xml", + libs: [ + "ims-common", + ], + kotlincflags: ["-Xjvm-default=enable"], plugins: ["dagger2-compiler"], @@ -234,6 +239,7 @@ android_library { "android.test.runner", "android.test.base", "android.test.mock", + "ims-common", ], kotlincflags: ["-Xjvm-default=enable"], aaptflags: [ @@ -289,6 +295,10 @@ android_app { certificate: "platform", privileged: true, + libs: [ + "ims-common", + ], + kotlincflags: ["-Xjvm-default=enable"], dxflags: ["--multi-dex"], diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index 2737ecf5ffa6..5f0fbe6de47d 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -72,6 +72,7 @@ + @@ -329,10 +330,22 @@ + + + + + + + + + + + + + + + + + + + + + + + @@ -436,6 +462,20 @@ android:resource="@string/summary_empty"/> + + + + + + + + + + + + + + diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/Interpolators.java b/packages/SystemUI/animation/src/com/android/systemui/animation/Interpolators.java index 80634832acd9..330a91de4674 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/Interpolators.java +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/Interpolators.java @@ -128,6 +128,8 @@ public class Interpolators { */ public static final Interpolator FAST_OUT_SLOW_IN_REVERSE = new PathInterpolator(0.8f, 0f, 0.6f, 1f); + public static final Interpolator SLOWDOWN = + new PathInterpolator(0.5f, 1f, 0.5f, 1f); public static final Interpolator SLOW_OUT_LINEAR_IN = new PathInterpolator(0.8f, 0f, 1f, 1f); public static final Interpolator ALPHA_IN = new PathInterpolator(0.4f, 0f, 1f, 1f); public static final Interpolator ALPHA_OUT = new PathInterpolator(0f, 0f, 0.8f, 1f); diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/GlobalActions.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/GlobalActions.java index 95ff13b45fb9..7775551b731a 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/GlobalActions.java +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/GlobalActions.java @@ -26,7 +26,7 @@ public interface GlobalActions extends Plugin { int VERSION = 1; void showGlobalActions(GlobalActionsManager manager); - default void showShutdownUi(boolean isReboot, String reason) { + default void showShutdownUi(boolean isReboot, String reason, boolean rebootCustom) { } default void destroy() { @@ -40,6 +40,6 @@ public interface GlobalActionsManager { void onGlobalActionsHidden(); void shutdown(); - void reboot(boolean safeMode); + void reboot(boolean safeMode, String reason); } } diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/VolumeDialogController.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/VolumeDialogController.java index 894bb5fc8577..217b529ed35a 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/VolumeDialogController.java +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/VolumeDialogController.java @@ -106,6 +106,7 @@ public static final class State { public ComponentName effectsSuppressor; public String effectsSuppressorName; public int activeStream = NO_ACTIVE_STREAM; + public boolean linkedNotification; public boolean disallowAlarms; public boolean disallowMedia; public boolean disallowSystem; @@ -124,6 +125,7 @@ public State copy() { } rt.effectsSuppressorName = effectsSuppressorName; rt.activeStream = activeStream; + rt.linkedNotification = linkedNotification; rt.disallowAlarms = disallowAlarms; rt.disallowMedia = disallowMedia; rt.disallowSystem = disallowSystem; @@ -157,6 +159,7 @@ public String toString(int indent) { sep(sb, indent); sb.append("effectsSuppressor:").append(effectsSuppressor); sep(sb, indent); sb.append("effectsSuppressorName:").append(effectsSuppressorName); sep(sb, indent); sb.append("activeStream:").append(activeStream); + sep(sb, indent); sb.append("linkedNotification:").append(linkedNotification); sep(sb, indent); sb.append("disallowAlarms:").append(disallowAlarms); sep(sb, indent); sb.append("disallowMedia:").append(disallowMedia); sep(sb, indent); sb.append("disallowSystem:").append(disallowSystem); diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java index 2b169997168b..fe76ff4a7e42 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java @@ -154,7 +154,7 @@ public String toString() { @ProvidesInterface(version = State.VERSION) public static class State { public static final int VERSION = 1; - public static final int DEFAULT_STATE = Tile.STATE_ACTIVE; + public static final int DEFAULT_STATE = Tile.STATE_INACTIVE; public Icon icon; public Supplier iconSupplier; diff --git a/packages/SystemUI/proguard.flags b/packages/SystemUI/proguard.flags index 7538555e1bcd..b41952b7306b 100644 --- a/packages/SystemUI/proguard.flags +++ b/packages/SystemUI/proguard.flags @@ -10,6 +10,9 @@ } -keep class * extends com.android.systemui.CoreStartable -keep class * implements com.android.systemui.CoreStartable$Injector +-keep class * implements com.android.systemui.biometrics.UdfpsDisplayModeProvider { + public (...); +} # Needed for builds to properly initialize KeyFrames from xml scene -keepclassmembers class * extends androidx.constraintlayout.motion.widget.Key { diff --git a/packages/SystemUI/res-keyguard/layout/footer_actions.xml b/packages/SystemUI/res-keyguard/layout/footer_actions.xml index 1ce106ed2156..14027a279b77 100644 --- a/packages/SystemUI/res-keyguard/layout/footer_actions.xml +++ b/packages/SystemUI/res-keyguard/layout/footer_actions.xml @@ -95,7 +95,7 @@ android:padding="@dimen/qs_footer_icon_padding" android:src="@*android:drawable/ic_lock_power_off" android:contentDescription="@string/accessibility_quick_settings_power_menu" - android:tint="?androidprv:attr/textColorOnAccent" /> + android:tint="?androidprv:attr/textColorPrimaryInverse" /> - \ No newline at end of file + diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml index 3ad7c8c4369c..b3b416ef5d69 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml @@ -30,7 +30,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentStart="true" - android:layout_alignParentTop="true" + android:layout_below="@id/weather_container" android:paddingStart="@dimen/clock_padding_start" /> + + + + + + 24dp + 4dp + 14dp + diff --git a/packages/SystemUI/res-keyguard/values/strings.xml b/packages/SystemUI/res-keyguard/values/strings.xml index d90156d451c7..e33197007b08 100644 --- a/packages/SystemUI/res-keyguard/values/strings.xml +++ b/packages/SystemUI/res-keyguard/values/strings.xml @@ -87,9 +87,6 @@ h:mm kk:mm - - PIN area diff --git a/packages/SystemUI/res/drawable-hdpi/ic_camera_alt_24dp.png b/packages/SystemUI/res/drawable-hdpi/ic_camera_alt_24dp.png deleted file mode 100644 index 253c73792a72..000000000000 Binary files a/packages/SystemUI/res/drawable-hdpi/ic_camera_alt_24dp.png and /dev/null differ diff --git a/packages/SystemUI/res/drawable-mdpi/ic_camera_alt_24dp.png b/packages/SystemUI/res/drawable-mdpi/ic_camera_alt_24dp.png deleted file mode 100644 index ee1187bfe2e9..000000000000 Binary files a/packages/SystemUI/res/drawable-mdpi/ic_camera_alt_24dp.png and /dev/null differ diff --git a/packages/SystemUI/res/drawable-nodpi/udfps_icon_pressed.png b/packages/SystemUI/res/drawable-nodpi/udfps_icon_pressed.png new file mode 100644 index 000000000000..4102e28c1300 Binary files /dev/null and b/packages/SystemUI/res/drawable-nodpi/udfps_icon_pressed.png differ diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_camera_alt_24dp.png b/packages/SystemUI/res/drawable-xhdpi/ic_camera_alt_24dp.png deleted file mode 100644 index 268eba0dde54..000000000000 Binary files a/packages/SystemUI/res/drawable-xhdpi/ic_camera_alt_24dp.png and /dev/null differ diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_camera_alt_24dp.png b/packages/SystemUI/res/drawable-xxhdpi/ic_camera_alt_24dp.png deleted file mode 100644 index 9175118fad0f..000000000000 Binary files a/packages/SystemUI/res/drawable-xxhdpi/ic_camera_alt_24dp.png and /dev/null differ diff --git a/packages/SystemUI/res/drawable-xxxhdpi/ic_camera_alt_24dp.png b/packages/SystemUI/res/drawable-xxxhdpi/ic_camera_alt_24dp.png deleted file mode 100644 index 20e26b8291f5..000000000000 Binary files a/packages/SystemUI/res/drawable-xxxhdpi/ic_camera_alt_24dp.png and /dev/null differ diff --git a/packages/SystemUI/res/drawable/android.xml b/packages/SystemUI/res/drawable/android.xml index 750de057439d..ce938ebd7d53 100644 --- a/packages/SystemUI/res/drawable/android.xml +++ b/packages/SystemUI/res/drawable/android.xml @@ -1,5 +1,5 @@ + + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> - - - + android:fillColor="#000000" + android:pathData="M17.6 11.48l1.84-3.18a0.63 0.63 0 0 0-1.09-0.63l-1.88 3.24a11.43 11.43 0 0 0-8.94 0L5.65 7.67A0.63 0.63 0 0 0 4.56 8.3l1.84 3.18A10.81 10.81 0 0 0 1 20h22a10.81 10.81 0 0 0-5.4-8.52zM7 17.25A1.25 1.25 0 1 1 8.25 16 1.25 1.25 0 0 1 7 17.25zm10 0A1.25 1.25 0 1 1 18.25 16 1.25 1.25 0 0 1 17 17.25z"/> diff --git a/packages/SystemUI/res/drawable/brightness_bg.xml b/packages/SystemUI/res/drawable/brightness_bg.xml new file mode 100644 index 000000000000..7e1a9a0d282f --- /dev/null +++ b/packages/SystemUI/res/drawable/brightness_bg.xml @@ -0,0 +1,22 @@ + + + + + + + + diff --git a/packages/SystemUI/res/drawable/brightness_progress_full_drawable.xml b/packages/SystemUI/res/drawable/brightness_progress_full_drawable.xml index 4d9188c40822..1eae667c6e79 100644 --- a/packages/SystemUI/res/drawable/brightness_progress_full_drawable.xml +++ b/packages/SystemUI/res/drawable/brightness_progress_full_drawable.xml @@ -22,7 +22,7 @@ android:height="@dimen/rounded_slider_height"> - + diff --git a/packages/SystemUI/res/drawable/dialog_tri_state_down_bg.xml b/packages/SystemUI/res/drawable/dialog_tri_state_down_bg.xml new file mode 100644 index 000000000000..7dddc9d0e930 --- /dev/null +++ b/packages/SystemUI/res/drawable/dialog_tri_state_down_bg.xml @@ -0,0 +1,29 @@ + + + + + + + + + + \ No newline at end of file diff --git a/packages/SystemUI/res/drawable/dialog_tri_state_middle_bg.xml b/packages/SystemUI/res/drawable/dialog_tri_state_middle_bg.xml new file mode 100644 index 000000000000..7cde6be2808c --- /dev/null +++ b/packages/SystemUI/res/drawable/dialog_tri_state_middle_bg.xml @@ -0,0 +1,29 @@ + + + + + + + + + + \ No newline at end of file diff --git a/packages/SystemUI/res/drawable/dialog_tri_state_up_bg.xml b/packages/SystemUI/res/drawable/dialog_tri_state_up_bg.xml new file mode 100644 index 000000000000..69757a77ee86 --- /dev/null +++ b/packages/SystemUI/res/drawable/dialog_tri_state_up_bg.xml @@ -0,0 +1,29 @@ + + + + + + + + + + \ No newline at end of file diff --git a/packages/SystemUI/res/drawable/fingerprint_bg.xml b/packages/SystemUI/res/drawable/fingerprint_bg.xml index 558ec08b2ceb..2022cef4928b 100644 --- a/packages/SystemUI/res/drawable/fingerprint_bg.xml +++ b/packages/SystemUI/res/drawable/fingerprint_bg.xml @@ -18,7 +18,7 @@ android:shape="oval"> + android:color="@color/keyguard_button_bg_color"/> + + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#000000" + android:pathData="M17.6 11.48l1.84-3.18a0.63 0.63 0 0 0-1.09-0.63l-1.88 3.24a11.43 11.43 0 0 0-8.94 0L5.65 7.67A0.63 0.63 0 0 0 4.56 8.3l1.84 3.18A10.81 10.81 0 0 0 1 20h22a10.81 10.81 0 0 0-5.4-8.52zM7 17.25A1.25 1.25 0 1 1 8.25 16 1.25 1.25 0 0 1 7 17.25zm10 0A1.25 1.25 0 1 1 18.25 16 1.25 1.25 0 0 1 17 17.25z"/> diff --git a/packages/SystemUI/res/drawable/ic_app_volume.xml b/packages/SystemUI/res/drawable/ic_app_volume.xml new file mode 100644 index 000000000000..c65a4c3ba017 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_app_volume.xml @@ -0,0 +1,37 @@ + + + + + + + diff --git a/packages/SystemUI/res/drawable/ic_bluetooth_connected.xml b/packages/SystemUI/res/drawable/ic_bluetooth_connected.xml index 125082c49bdc..1e5ae49f2e25 100644 --- a/packages/SystemUI/res/drawable/ic_bluetooth_connected.xml +++ b/packages/SystemUI/res/drawable/ic_bluetooth_connected.xml @@ -1,30 +1,11 @@ - + + android:viewportWidth="24" + android:viewportHeight="24"> - - - \ No newline at end of file + android:fillColor="#000000" + android:pathData="M12.1 2.111c0.254 0 0.5 0.088 0.697 0.249l5.148 4.212c0.513 0.42 0.588 1.176 0.169 1.689L18.08 8.298l-0.045 0.05-0.087 0.078L13.623 12l4.326 3.574c0.511 0.422 0.583 1.179 0.161 1.69-0.077 0.087-0.077 0.087-0.165 0.164l-5.148 4.212c-0.47 0.385-1.164 0.316-1.548-0.155C11.088 21.29 11 21.043 11 20.79v-6.622L6.637 17.77l-1.274-1.542L10.483 12l-5.12-4.229 1.274-1.542L11 9.833V3.211c0-0.607 0.492-1.1 1.1-1.1zM13 14.08v4.81l2.925-2.394L13 14.08zm6-3.494L20.414 12 19 13.414 17.586 12 19 10.586zm-14 0L6.414 12 5 13.414 3.586 12 5 10.586zm8-5.476v4.81l2.925-2.416L13 5.11z" + android:strokeWidth="1" /> + diff --git a/packages/SystemUI/res/drawable/ic_brightness_full.xml b/packages/SystemUI/res/drawable/ic_brightness_full.xml index f44333236a12..a2cc44953a15 100644 --- a/packages/SystemUI/res/drawable/ic_brightness_full.xml +++ b/packages/SystemUI/res/drawable/ic_brightness_full.xml @@ -14,6 +14,7 @@ Copyright (C) 2020 The Android Open Source Project limitations under the License. --> + android:fillColor="?androidprv:attr/colorSurface" /> diff --git a/packages/SystemUI/res/drawable/ic_brightness_low.xml b/packages/SystemUI/res/drawable/ic_brightness_low.xml index b463556e20d0..06ac4625576d 100644 --- a/packages/SystemUI/res/drawable/ic_brightness_low.xml +++ b/packages/SystemUI/res/drawable/ic_brightness_low.xml @@ -1,10 +1,11 @@ diff --git a/packages/SystemUI/res/drawable/ic_brightness_medium.xml b/packages/SystemUI/res/drawable/ic_brightness_medium.xml index 80acc4d565fa..50ecfd77ba5c 100644 --- a/packages/SystemUI/res/drawable/ic_brightness_medium.xml +++ b/packages/SystemUI/res/drawable/ic_brightness_medium.xml @@ -1,10 +1,11 @@ diff --git a/packages/SystemUI/res/drawable/ic_camera_alt_24dp.xml b/packages/SystemUI/res/drawable/ic_camera_alt_24dp.xml new file mode 100644 index 000000000000..dfd8d04472bc --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_camera_alt_24dp.xml @@ -0,0 +1,25 @@ + + + + + diff --git a/packages/SystemUI/res/drawable/ic_flashlight_off.xml b/packages/SystemUI/res/drawable/ic_flashlight_off.xml new file mode 100644 index 000000000000..930adb03f6e4 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_flashlight_off.xml @@ -0,0 +1,10 @@ + + + diff --git a/packages/SystemUI/res/drawable/ic_flashlight_on.xml b/packages/SystemUI/res/drawable/ic_flashlight_on.xml new file mode 100644 index 000000000000..bca834e09d0f --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_flashlight_on.xml @@ -0,0 +1,10 @@ + + + diff --git a/packages/SystemUI/res/drawable/ic_force_stop.xml b/packages/SystemUI/res/drawable/ic_force_stop.xml new file mode 100644 index 000000000000..eda6079c48fd --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_force_stop.xml @@ -0,0 +1,31 @@ + + + + + + + diff --git a/packages/SystemUI/res/drawable/ic_lock_screenshot.xml b/packages/SystemUI/res/drawable/ic_lock_screenshot.xml new file mode 100644 index 000000000000..316f576b433c --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_lock_screenshot.xml @@ -0,0 +1,11 @@ + + + + diff --git a/packages/SystemUI/res/drawable/ic_lock_settings.xml b/packages/SystemUI/res/drawable/ic_lock_settings.xml new file mode 100644 index 000000000000..0cdb6d131827 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_lock_settings.xml @@ -0,0 +1,11 @@ + + + + diff --git a/packages/SystemUI/res/drawable/ic_lock_user.xml b/packages/SystemUI/res/drawable/ic_lock_user.xml new file mode 100644 index 000000000000..59845351b6fa --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_lock_user.xml @@ -0,0 +1,11 @@ + + + + diff --git a/packages/SystemUI/res/drawable/ic_qs_brightness_auto_off.xml b/packages/SystemUI/res/drawable/ic_qs_brightness_auto_off.xml new file mode 100644 index 000000000000..3f9abb955222 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_qs_brightness_auto_off.xml @@ -0,0 +1,23 @@ + + + + + diff --git a/packages/SystemUI/res/drawable/ic_qs_brightness_auto_on.xml b/packages/SystemUI/res/drawable/ic_qs_brightness_auto_on.xml new file mode 100644 index 000000000000..4de4a0d8c7e9 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_qs_brightness_auto_on.xml @@ -0,0 +1,23 @@ + + + + + diff --git a/packages/SystemUI/res/drawable/ic_qs_caffeine.xml b/packages/SystemUI/res/drawable/ic_qs_caffeine.xml new file mode 100644 index 000000000000..ee8bec99a310 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_qs_caffeine.xml @@ -0,0 +1,26 @@ + + + + + + diff --git a/packages/SystemUI/res/drawable/ic_qs_compass.xml b/packages/SystemUI/res/drawable/ic_qs_compass.xml new file mode 100644 index 000000000000..ce0d3988a591 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_qs_compass.xml @@ -0,0 +1,31 @@ + + + + + + diff --git a/packages/SystemUI/res/drawable/ic_qs_cpu_info.xml b/packages/SystemUI/res/drawable/ic_qs_cpu_info.xml new file mode 100644 index 000000000000..78738b142e9a --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_qs_cpu_info.xml @@ -0,0 +1,10 @@ + + + + diff --git a/packages/SystemUI/res/drawable/ic_qs_data_switch_0.xml b/packages/SystemUI/res/drawable/ic_qs_data_switch_0.xml new file mode 100644 index 000000000000..81b58abe42b3 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_qs_data_switch_0.xml @@ -0,0 +1,4 @@ + + + diff --git a/packages/SystemUI/res/drawable/ic_qs_data_switch_1.xml b/packages/SystemUI/res/drawable/ic_qs_data_switch_1.xml new file mode 100644 index 000000000000..dea973a4811d --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_qs_data_switch_1.xml @@ -0,0 +1,19 @@ + + + + + + diff --git a/packages/SystemUI/res/drawable/ic_qs_data_switch_2.xml b/packages/SystemUI/res/drawable/ic_qs_data_switch_2.xml new file mode 100644 index 000000000000..037e3f108597 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_qs_data_switch_2.xml @@ -0,0 +1,19 @@ + + + + + + diff --git a/packages/SystemUI/res/drawable/ic_qs_smart_pixels.xml b/packages/SystemUI/res/drawable/ic_qs_smart_pixels.xml new file mode 100644 index 000000000000..525321baff96 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_qs_smart_pixels.xml @@ -0,0 +1,8 @@ + + + + diff --git a/packages/SystemUI/res/drawable/ic_qs_weather_default_off.xml b/packages/SystemUI/res/drawable/ic_qs_weather_default_off.xml new file mode 100644 index 000000000000..9b48fee51e86 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_qs_weather_default_off.xml @@ -0,0 +1,9 @@ + + + + diff --git a/packages/SystemUI/res/drawable/ic_qs_weather_default_off_white.xml b/packages/SystemUI/res/drawable/ic_qs_weather_default_off_white.xml new file mode 100644 index 000000000000..70d8729da2a1 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_qs_weather_default_off_white.xml @@ -0,0 +1,8 @@ + + + + diff --git a/packages/SystemUI/res/drawable/ic_qs_weather_default_on.xml b/packages/SystemUI/res/drawable/ic_qs_weather_default_on.xml new file mode 100644 index 000000000000..edd144fcef82 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_qs_weather_default_on.xml @@ -0,0 +1,10 @@ + + + diff --git a/packages/SystemUI/res/drawable/ic_restart_bootloader.xml b/packages/SystemUI/res/drawable/ic_restart_bootloader.xml new file mode 100644 index 000000000000..0381d5886052 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_restart_bootloader.xml @@ -0,0 +1,26 @@ + + + + + + diff --git a/packages/SystemUI/res/drawable/ic_restart_recovery.xml b/packages/SystemUI/res/drawable/ic_restart_recovery.xml new file mode 100644 index 000000000000..d9c24303fd81 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_restart_recovery.xml @@ -0,0 +1,26 @@ + + + + + + diff --git a/packages/SystemUI/res/drawable/ic_restart_system.xml b/packages/SystemUI/res/drawable/ic_restart_system.xml new file mode 100644 index 000000000000..3ec2fc825031 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_restart_system.xml @@ -0,0 +1,26 @@ + + + + + + diff --git a/packages/SystemUI/res/drawable/ic_sr_clock.xml b/packages/SystemUI/res/drawable/ic_sr_clock.xml new file mode 100644 index 000000000000..0a4b7d81285a --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_sr_clock.xml @@ -0,0 +1,9 @@ + + + diff --git a/packages/SystemUI/res/drawable/ic_sr_quality.xml b/packages/SystemUI/res/drawable/ic_sr_quality.xml new file mode 100644 index 000000000000..a608bb4a88f5 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_sr_quality.xml @@ -0,0 +1,31 @@ + + + + + + + diff --git a/packages/SystemUI/res/drawable/ic_sr_stopdot.xml b/packages/SystemUI/res/drawable/ic_sr_stopdot.xml new file mode 100644 index 000000000000..544f2d9713d9 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_sr_stopdot.xml @@ -0,0 +1,26 @@ + + + + + diff --git a/packages/SystemUI/res/drawable/ic_statusbar_airplane.xml b/packages/SystemUI/res/drawable/ic_statusbar_airplane.xml new file mode 100644 index 000000000000..7ade8b215590 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_statusbar_airplane.xml @@ -0,0 +1,25 @@ + + + + diff --git a/packages/SystemUI/res/drawable/ic_statusbar_alarm.xml b/packages/SystemUI/res/drawable/ic_statusbar_alarm.xml new file mode 100644 index 000000000000..4d0a5f8c7346 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_statusbar_alarm.xml @@ -0,0 +1,25 @@ + + + + diff --git a/packages/SystemUI/res/drawable/ic_statusbar_auto_rotate.xml b/packages/SystemUI/res/drawable/ic_statusbar_auto_rotate.xml new file mode 100644 index 000000000000..9a2cb496d282 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_statusbar_auto_rotate.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/packages/SystemUI/res/drawable/ic_statusbar_bluetooth.xml b/packages/SystemUI/res/drawable/ic_statusbar_bluetooth.xml new file mode 100644 index 000000000000..91fcff0dfab6 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_statusbar_bluetooth.xml @@ -0,0 +1,25 @@ + + + + \ No newline at end of file diff --git a/packages/SystemUI/res/drawable/ic_statusbar_camera.xml b/packages/SystemUI/res/drawable/ic_statusbar_camera.xml new file mode 100644 index 000000000000..32d6b5326ffc --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_statusbar_camera.xml @@ -0,0 +1,25 @@ + + + + diff --git a/packages/SystemUI/res/drawable/ic_statusbar_cast.xml b/packages/SystemUI/res/drawable/ic_statusbar_cast.xml new file mode 100644 index 000000000000..5413c77215df --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_statusbar_cast.xml @@ -0,0 +1,25 @@ + + + + diff --git a/packages/SystemUI/res/drawable/ic_statusbar_clock.xml b/packages/SystemUI/res/drawable/ic_statusbar_clock.xml new file mode 100644 index 000000000000..4b91508d2eb1 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_statusbar_clock.xml @@ -0,0 +1,28 @@ + + + + + diff --git a/packages/SystemUI/res/drawable/ic_statusbar_data_saver.xml b/packages/SystemUI/res/drawable/ic_statusbar_data_saver.xml new file mode 100644 index 000000000000..f2bc5adca3e9 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_statusbar_data_saver.xml @@ -0,0 +1,26 @@ + + + + + diff --git a/packages/SystemUI/res/drawable/ic_statusbar_do_not_disturb.xml b/packages/SystemUI/res/drawable/ic_statusbar_do_not_disturb.xml new file mode 100644 index 000000000000..cace8d4433f5 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_statusbar_do_not_disturb.xml @@ -0,0 +1,28 @@ + + + + + diff --git a/packages/SystemUI/res/drawable/ic_statusbar_ethernet.xml b/packages/SystemUI/res/drawable/ic_statusbar_ethernet.xml new file mode 100644 index 000000000000..46e753fd7eb8 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_statusbar_ethernet.xml @@ -0,0 +1,34 @@ + + + + + + + diff --git a/packages/SystemUI/res/drawable/ic_statusbar_headset.xml b/packages/SystemUI/res/drawable/ic_statusbar_headset.xml new file mode 100644 index 000000000000..ad9a55fcc5e2 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_statusbar_headset.xml @@ -0,0 +1,25 @@ + + + + diff --git a/packages/SystemUI/res/drawable/ic_statusbar_hotspot.xml b/packages/SystemUI/res/drawable/ic_statusbar_hotspot.xml new file mode 100644 index 000000000000..37f5ea9075ae --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_statusbar_hotspot.xml @@ -0,0 +1,32 @@ + + + + + + + diff --git a/packages/SystemUI/res/drawable/ic_statusbar_mobile_network.xml b/packages/SystemUI/res/drawable/ic_statusbar_mobile_network.xml new file mode 100644 index 000000000000..1f24717a1512 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_statusbar_mobile_network.xml @@ -0,0 +1,26 @@ + + + + diff --git a/packages/SystemUI/res/drawable/ic_statusbar_mute.xml b/packages/SystemUI/res/drawable/ic_statusbar_mute.xml new file mode 100644 index 000000000000..c63aeea9489e --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_statusbar_mute.xml @@ -0,0 +1,26 @@ + + + + + diff --git a/packages/SystemUI/res/drawable/ic_statusbar_nfc.xml b/packages/SystemUI/res/drawable/ic_statusbar_nfc.xml new file mode 100644 index 000000000000..4e7af00a22c1 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_statusbar_nfc.xml @@ -0,0 +1,31 @@ + + + + diff --git a/packages/SystemUI/res/drawable/ic_statusbar_roaming.xml b/packages/SystemUI/res/drawable/ic_statusbar_roaming.xml new file mode 100644 index 000000000000..b8d2be6543ab --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_statusbar_roaming.xml @@ -0,0 +1,25 @@ + + + + diff --git a/packages/SystemUI/res/drawable/ic_statusbar_volte.xml b/packages/SystemUI/res/drawable/ic_statusbar_volte.xml new file mode 100644 index 000000000000..6b81bcdfba0b --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_statusbar_volte.xml @@ -0,0 +1,19 @@ + + + + + + diff --git a/packages/SystemUI/res/drawable/ic_statusbar_vpn.xml b/packages/SystemUI/res/drawable/ic_statusbar_vpn.xml new file mode 100644 index 000000000000..04c38ec993c3 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_statusbar_vpn.xml @@ -0,0 +1,29 @@ + + + + + + diff --git a/packages/SystemUI/res/drawable/ic_statusbar_wifi.xml b/packages/SystemUI/res/drawable/ic_statusbar_wifi.xml new file mode 100644 index 000000000000..e56e9512c9b5 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_statusbar_wifi.xml @@ -0,0 +1,26 @@ + + + + + diff --git a/packages/SystemUI/res/drawable/ic_statusbar_work.xml b/packages/SystemUI/res/drawable/ic_statusbar_work.xml new file mode 100644 index 000000000000..ea8918b1ca12 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_statusbar_work.xml @@ -0,0 +1,26 @@ + + + + + diff --git a/packages/SystemUI/res/drawable/ic_storage.xml b/packages/SystemUI/res/drawable/ic_storage.xml new file mode 100644 index 000000000000..12d8f1c5c497 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_storage.xml @@ -0,0 +1,25 @@ + + + + diff --git a/packages/SystemUI/res/drawable/ic_sysbar_home.xml b/packages/SystemUI/res/drawable/ic_sysbar_home.xml index da239372791f..c88aaf648a5a 100644 --- a/packages/SystemUI/res/drawable/ic_sysbar_home.xml +++ b/packages/SystemUI/res/drawable/ic_sysbar_home.xml @@ -17,10 +17,13 @@ + android:viewportWidth="96" + android:viewportHeight="96"> + android:pathData="M48 83.6c-19.6 0 -35.6 -16 -35.6 -35.6 0 -19.6 16 -35.6 35.6 -35.6 19.6 0 35.6 16 35.6 35.6l0 0.2C83.4 67.8 67.6 83.4 48 83.6ZM77.8 48.2C77.6 32 64.8 18.8 48.4 18.2c-16.2 0 -29.6 12.8 -30.2 29 0 16.6 13.2 30 29.6 30.4 16.4 -0.2 29.6 -13.2 30 -29.4z" /> + \ No newline at end of file diff --git a/packages/SystemUI/res/drawable/ic_volte.xml b/packages/SystemUI/res/drawable/ic_volte.xml new file mode 100644 index 000000000000..0597b0eb0141 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_volte.xml @@ -0,0 +1,40 @@ + + + + + + + \ No newline at end of file diff --git a/packages/SystemUI/res/drawable/ic_volte_no_voice.xml b/packages/SystemUI/res/drawable/ic_volte_no_voice.xml new file mode 100644 index 000000000000..490b624927f0 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_volte_no_voice.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/packages/SystemUI/res/drawable/ic_volume_bt_sco.xml b/packages/SystemUI/res/drawable/ic_volume_bt_sco.xml index 9ed77fe2391d..ba15b53639c0 100644 --- a/packages/SystemUI/res/drawable/ic_volume_bt_sco.xml +++ b/packages/SystemUI/res/drawable/ic_volume_bt_sco.xml @@ -22,9 +22,6 @@ - + android:pathData="M20 15.5C18.8 15.5 17.5 15.3 16.4 14.9H16.1C15.8 14.9 15.6 15 15.4 15.2L13.2 17.4C10.4 15.9 8 13.6 6.6 10.8L8.8 8.6C9.1 8.3 9.2 7.9 9 7.6C8.7 6.5 8.5 5.2 8.5 4C8.5 3.5 8 3 7.5 3H4C3.5 3 3 3.5 3 4C3 13.4 10.6 21 20 21C20.5 21 21 20.5 21 20V16.5C21 16 20.5 15.5 20 15.5M5 5H6.5C6.6 5.9 6.8 6.8 7 7.6L5.8 8.8C5.4 7.6 5.1 6.3 5 5M19 19C17.7 18.9 16.4 18.6 15.2 18.2L16.4 17C17.2 17.2 18.1 17.4 19 17.4V19M18 7.21L18.94 8.14L18 9.08M18 2.91L18.94 3.85L18 4.79M14.71 9.5L17 7.21V11H17.5L20.35 8.14L18.21 6L20.35 3.85L17.5 1H17V4.79L14.71 2.5L14 3.21L16.79 6L14 8.79L14.71 9.5Z" /> diff --git a/packages/SystemUI/res/drawable/ic_volume_media.xml b/packages/SystemUI/res/drawable/ic_volume_media.xml index bffb5a4314d8..d7953bab9f3e 100644 --- a/packages/SystemUI/res/drawable/ic_volume_media.xml +++ b/packages/SystemUI/res/drawable/ic_volume_media.xml @@ -22,6 +22,6 @@ + android:pathData="M12,3l0.01,10.55c-0.59,-0.34 -1.27,-0.55 -2,-0.55C7.79,13 6,14.79 6,17c0,2.21 1.79,4 4.01,4S14,19.21 14,17V7h4V3H12zM10.01,19c-1.1,0 -2,-0.9 -2,-2c0,-1.1 0.9,-2 2,-2s2,0.9 2,2C12.01,18.1 11.11,19 10.01,19z"/> diff --git a/packages/SystemUI/res/drawable/ic_volume_media_bt.xml b/packages/SystemUI/res/drawable/ic_volume_media_bt.xml index ea467c6519f4..4192699964fd 100644 --- a/packages/SystemUI/res/drawable/ic_volume_media_bt.xml +++ b/packages/SystemUI/res/drawable/ic_volume_media_bt.xml @@ -21,5 +21,5 @@ android:tint="?android:attr/textColorPrimary"> + android:pathData="M9,3l0.01,10.55C8.41,13.21 7.73,13 7.01,13C4.79,13 3,14.79 3,17c0,2.21 1.79,4 4.01,4S11,19.21 11,17V7h4V3H9zM7.01,19c-1.1,0 -2,-0.9 -2,-2c0,-1.1 0.9,-2 2,-2s2,0.9 2,2C9.01,18.1 8.11,19 7.01,19zM21,12.43L17.57,9h-0.6v4.55l-2.75,-2.75l-0.85,0.85L16.73,15l-3.35,3.35l0.85,0.85l2.75,-2.75V21h0.6L21,17.57L18.42,15L21,12.43zM18.17,11.3l1.13,1.13l-1.13,1.13V11.3zM19.3,17.57l-1.13,1.13v-2.26L19.3,17.57z"/> \ No newline at end of file diff --git a/packages/SystemUI/res/drawable/ic_volume_media_bt_mute.xml b/packages/SystemUI/res/drawable/ic_volume_media_bt_mute.xml index f01e49fb9177..4db0316790bb 100644 --- a/packages/SystemUI/res/drawable/ic_volume_media_bt_mute.xml +++ b/packages/SystemUI/res/drawable/ic_volume_media_bt_mute.xml @@ -21,5 +21,5 @@ android:tint="?android:attr/textColorPrimary"> + android:pathData="M9,6.17L9,3h6v4h-4v1.17L9,6.17zM19.42,15L22,17.57l-0.8,0.8l-6.78,-6.78l0.8,-0.8l2.75,2.75V9h0.6L22,12.43L19.42,15zM19.17,13.55l1.13,-1.13l-1.13,-1.13V13.55zM17.21,17.21l3.98,3.98l-1.41,1.41l-3.98,-3.98l-0.58,0.58l-0.85,-0.85l0.58,-0.58L11,13.83V17c0,2.21 -1.78,4 -3.99,4S3,19.21 3,17c0,-2.21 1.79,-4 4.01,-4c0.73,0 1.41,0.21 2,0.55l0,-1.72L1.39,4.22l1.41,-1.41l13.56,13.56L17.21,17.21zM9.01,17c0,-1.1 -0.9,-2 -2,-2s-2,0.9 -2,2c0,1.1 0.9,2 2,2S9.01,18.1 9.01,17z"/> \ No newline at end of file diff --git a/packages/SystemUI/res/drawable/ic_volume_media_mute.xml b/packages/SystemUI/res/drawable/ic_volume_media_mute.xml index 63dfe8f230dd..12837b5c29b6 100644 --- a/packages/SystemUI/res/drawable/ic_volume_media_mute.xml +++ b/packages/SystemUI/res/drawable/ic_volume_media_mute.xml @@ -22,6 +22,10 @@ + android:pathData="M21.19,21.19L14,14l-2,-2l-9.2,-9.2L1.39,4.22l8.79,8.79c-0.06,0 -0.12,-0.01 -0.18,-0.01C7.79,13 6,14.79 6,17c0,2.21 1.79,4 4.01,4S14,19.21 14,17v-0.17l5.78,5.78L21.19,21.19zM10.01,19c-1.1,0 -2,-0.9 -2,-2c0,-1.1 0.9,-2 2,-2s2,0.9 2,2C12.01,18.1 11.11,19 10.01,19z"/> + + diff --git a/packages/SystemUI/res/drawable/ic_volume_notification.xml b/packages/SystemUI/res/drawable/ic_volume_notification.xml new file mode 100644 index 000000000000..42eb7b7f3f88 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_volume_notification.xml @@ -0,0 +1,30 @@ + + + + + + + diff --git a/packages/SystemUI/res/drawable/ic_volume_notification_mute.xml b/packages/SystemUI/res/drawable/ic_volume_notification_mute.xml new file mode 100644 index 000000000000..9cb7ca3679b9 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_volume_notification_mute.xml @@ -0,0 +1,33 @@ + + + + + + + + diff --git a/packages/SystemUI/res/drawable/ic_volume_ringer.xml b/packages/SystemUI/res/drawable/ic_volume_ringer.xml index 998344f019b7..62e0f40047ed 100644 --- a/packages/SystemUI/res/drawable/ic_volume_ringer.xml +++ b/packages/SystemUI/res/drawable/ic_volume_ringer.xml @@ -1,5 +1,6 @@ + + + diff --git a/packages/SystemUI/res/drawable/ic_wifi_standard_5.xml b/packages/SystemUI/res/drawable/ic_wifi_standard_5.xml new file mode 100755 index 000000000000..29137d81a8a0 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_wifi_standard_5.xml @@ -0,0 +1,25 @@ + + + + + diff --git a/packages/SystemUI/res/drawable/ic_wifi_standard_6.xml b/packages/SystemUI/res/drawable/ic_wifi_standard_6.xml new file mode 100755 index 000000000000..12adf97743c1 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_wifi_standard_6.xml @@ -0,0 +1,25 @@ + + + + + diff --git a/packages/SystemUI/res/drawable/keyguard_bottom_affordance_bg.xml b/packages/SystemUI/res/drawable/keyguard_bottom_affordance_bg.xml index 41123c84ded1..b09a5571e368 100644 --- a/packages/SystemUI/res/drawable/keyguard_bottom_affordance_bg.xml +++ b/packages/SystemUI/res/drawable/keyguard_bottom_affordance_bg.xml @@ -20,7 +20,7 @@ xmlns:android="http://schemas.android.com/apk/res/android" xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" android:shape="rectangle"> - + diff --git a/packages/SystemUI/res/drawable/qs_alike_top.xml b/packages/SystemUI/res/drawable/qs_alike_top.xml new file mode 100644 index 000000000000..ac1cabf636e5 --- /dev/null +++ b/packages/SystemUI/res/drawable/qs_alike_top.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + diff --git a/packages/SystemUI/res/drawable/qs_footer_actions_background.xml b/packages/SystemUI/res/drawable/qs_footer_actions_background.xml index c9517cd905dc..03ff64705ba8 100644 --- a/packages/SystemUI/res/drawable/qs_footer_actions_background.xml +++ b/packages/SystemUI/res/drawable/qs_footer_actions_background.xml @@ -15,8 +15,10 @@ --> - + + android:topRightRadius="@dimen/qs_corner_radius" + android:bottomLeftRadius="@dimen/qs_corner_radius" + android:bottomRightRadius="@dimen/qs_corner_radius"/> diff --git a/packages/SystemUI/res/drawable/qs_security_footer_background.xml b/packages/SystemUI/res/drawable/qs_security_footer_background.xml index 381af503d47c..ee36265af141 100644 --- a/packages/SystemUI/res/drawable/qs_security_footer_background.xml +++ b/packages/SystemUI/res/drawable/qs_security_footer_background.xml @@ -28,8 +28,7 @@ - + diff --git a/packages/SystemUI/res/drawable/reticker_background.xml b/packages/SystemUI/res/drawable/reticker_background.xml new file mode 100644 index 000000000000..37788cb91778 --- /dev/null +++ b/packages/SystemUI/res/drawable/reticker_background.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/packages/SystemUI/res/drawable/rounded_ripple.xml b/packages/SystemUI/res/drawable/rounded_ripple.xml index d9ed8233d886..8557ed31f43b 100644 --- a/packages/SystemUI/res/drawable/rounded_ripple.xml +++ b/packages/SystemUI/res/drawable/rounded_ripple.xml @@ -18,13 +18,13 @@ - + - + - \ No newline at end of file + diff --git a/packages/SystemUI/res/drawable/screenrecord_dot.xml b/packages/SystemUI/res/drawable/screenrecord_dot.xml new file mode 100644 index 000000000000..c2c796b53136 --- /dev/null +++ b/packages/SystemUI/res/drawable/screenrecord_dot.xml @@ -0,0 +1,24 @@ + + + + diff --git a/packages/SystemUI/res/drawable/stat_sys_data_bluetooth_connected_battery_0.xml b/packages/SystemUI/res/drawable/stat_sys_data_bluetooth_connected_battery_0.xml new file mode 100644 index 000000000000..c249d0f9fe3d --- /dev/null +++ b/packages/SystemUI/res/drawable/stat_sys_data_bluetooth_connected_battery_0.xml @@ -0,0 +1,16 @@ + + + + + + diff --git a/packages/SystemUI/res/drawable/stat_sys_data_bluetooth_connected_battery_1.xml b/packages/SystemUI/res/drawable/stat_sys_data_bluetooth_connected_battery_1.xml new file mode 100644 index 000000000000..b439a6e80ee7 --- /dev/null +++ b/packages/SystemUI/res/drawable/stat_sys_data_bluetooth_connected_battery_1.xml @@ -0,0 +1,21 @@ + + + + + + + diff --git a/packages/SystemUI/res/drawable/stat_sys_data_bluetooth_connected_battery_10.xml b/packages/SystemUI/res/drawable/stat_sys_data_bluetooth_connected_battery_10.xml new file mode 100644 index 000000000000..95d6cda11ad9 --- /dev/null +++ b/packages/SystemUI/res/drawable/stat_sys_data_bluetooth_connected_battery_10.xml @@ -0,0 +1,16 @@ + + + + + + diff --git a/packages/SystemUI/res/drawable/stat_sys_data_bluetooth_connected_battery_2.xml b/packages/SystemUI/res/drawable/stat_sys_data_bluetooth_connected_battery_2.xml new file mode 100644 index 000000000000..75b1784c0936 --- /dev/null +++ b/packages/SystemUI/res/drawable/stat_sys_data_bluetooth_connected_battery_2.xml @@ -0,0 +1,21 @@ + + + + + + + diff --git a/packages/SystemUI/res/drawable/stat_sys_data_bluetooth_connected_battery_3.xml b/packages/SystemUI/res/drawable/stat_sys_data_bluetooth_connected_battery_3.xml new file mode 100644 index 000000000000..e6b419fbb268 --- /dev/null +++ b/packages/SystemUI/res/drawable/stat_sys_data_bluetooth_connected_battery_3.xml @@ -0,0 +1,16 @@ + + + + + + diff --git a/packages/SystemUI/res/drawable/stat_sys_data_bluetooth_connected_battery_4.xml b/packages/SystemUI/res/drawable/stat_sys_data_bluetooth_connected_battery_4.xml new file mode 100644 index 000000000000..5236e9cef9f9 --- /dev/null +++ b/packages/SystemUI/res/drawable/stat_sys_data_bluetooth_connected_battery_4.xml @@ -0,0 +1,16 @@ + + + + + + diff --git a/packages/SystemUI/res/drawable/stat_sys_data_bluetooth_connected_battery_5.xml b/packages/SystemUI/res/drawable/stat_sys_data_bluetooth_connected_battery_5.xml new file mode 100644 index 000000000000..abdc7a7580e8 --- /dev/null +++ b/packages/SystemUI/res/drawable/stat_sys_data_bluetooth_connected_battery_5.xml @@ -0,0 +1,31 @@ + + + + + + + + + diff --git a/packages/SystemUI/res/drawable/stat_sys_data_bluetooth_connected_battery_6.xml b/packages/SystemUI/res/drawable/stat_sys_data_bluetooth_connected_battery_6.xml new file mode 100644 index 000000000000..1f68309db522 --- /dev/null +++ b/packages/SystemUI/res/drawable/stat_sys_data_bluetooth_connected_battery_6.xml @@ -0,0 +1,16 @@ + + + + + + diff --git a/packages/SystemUI/res/drawable/stat_sys_data_bluetooth_connected_battery_7.xml b/packages/SystemUI/res/drawable/stat_sys_data_bluetooth_connected_battery_7.xml new file mode 100644 index 000000000000..650420bf3f65 --- /dev/null +++ b/packages/SystemUI/res/drawable/stat_sys_data_bluetooth_connected_battery_7.xml @@ -0,0 +1,21 @@ + + + + + + + diff --git a/packages/SystemUI/res/drawable/stat_sys_data_bluetooth_connected_battery_8.xml b/packages/SystemUI/res/drawable/stat_sys_data_bluetooth_connected_battery_8.xml new file mode 100644 index 000000000000..66a28cf82dbd --- /dev/null +++ b/packages/SystemUI/res/drawable/stat_sys_data_bluetooth_connected_battery_8.xml @@ -0,0 +1,16 @@ + + + + + + diff --git a/packages/SystemUI/res/drawable/stat_sys_data_bluetooth_connected_battery_9.xml b/packages/SystemUI/res/drawable/stat_sys_data_bluetooth_connected_battery_9.xml new file mode 100644 index 000000000000..1d3e91d87e0b --- /dev/null +++ b/packages/SystemUI/res/drawable/stat_sys_data_bluetooth_connected_battery_9.xml @@ -0,0 +1,16 @@ + + + + + + diff --git a/packages/SystemUI/res/drawable/stat_sys_nfc.xml b/packages/SystemUI/res/drawable/stat_sys_nfc.xml new file mode 100644 index 000000000000..68d8eb08001c --- /dev/null +++ b/packages/SystemUI/res/drawable/stat_sys_nfc.xml @@ -0,0 +1,36 @@ + + + + + + + diff --git a/packages/SystemUI/res/drawable/stat_sys_ringer_silent.xml b/packages/SystemUI/res/drawable/stat_sys_ringer_silent.xml index a8f0cc3a1d92..1a0599107f69 100644 --- a/packages/SystemUI/res/drawable/stat_sys_ringer_silent.xml +++ b/packages/SystemUI/res/drawable/stat_sys_ringer_silent.xml @@ -14,8 +14,8 @@ Copyright (C) 2015 The Android Open Source Project limitations under the License. --> + android:insetLeft="0.5dp" + android:insetRight="0.5dp"> \ No newline at end of file + android:insetLeft="0dp" + android:insetRight="0dp" + android:drawable="@drawable/ic_volume_ringer_vibrate" /> diff --git a/packages/SystemUI/res/drawable/stat_sys_volte.xml b/packages/SystemUI/res/drawable/stat_sys_volte.xml new file mode 100644 index 000000000000..f4db5a430c3b --- /dev/null +++ b/packages/SystemUI/res/drawable/stat_sys_volte.xml @@ -0,0 +1,18 @@ + + + + + + diff --git a/packages/SystemUI/res/drawable/stat_sys_volte_slot1.xml b/packages/SystemUI/res/drawable/stat_sys_volte_slot1.xml new file mode 100644 index 000000000000..6b5c83a3e507 --- /dev/null +++ b/packages/SystemUI/res/drawable/stat_sys_volte_slot1.xml @@ -0,0 +1,18 @@ + + + + + + diff --git a/packages/SystemUI/res/drawable/stat_sys_volte_slot12.xml b/packages/SystemUI/res/drawable/stat_sys_volte_slot12.xml new file mode 100644 index 000000000000..a1ac96a151d7 --- /dev/null +++ b/packages/SystemUI/res/drawable/stat_sys_volte_slot12.xml @@ -0,0 +1,18 @@ + + + + + + diff --git a/packages/SystemUI/res/drawable/stat_sys_volte_slot2.xml b/packages/SystemUI/res/drawable/stat_sys_volte_slot2.xml new file mode 100644 index 000000000000..b3127fb6b016 --- /dev/null +++ b/packages/SystemUI/res/drawable/stat_sys_volte_slot2.xml @@ -0,0 +1,18 @@ + + + + + + diff --git a/packages/SystemUI/res/drawable/stat_sys_vowifi.xml b/packages/SystemUI/res/drawable/stat_sys_vowifi.xml new file mode 100644 index 000000000000..02e5e779218f --- /dev/null +++ b/packages/SystemUI/res/drawable/stat_sys_vowifi.xml @@ -0,0 +1,18 @@ + + + + + + diff --git a/packages/SystemUI/res/drawable/stat_sys_vowifi_slot1.xml b/packages/SystemUI/res/drawable/stat_sys_vowifi_slot1.xml new file mode 100644 index 000000000000..cf54850c9414 --- /dev/null +++ b/packages/SystemUI/res/drawable/stat_sys_vowifi_slot1.xml @@ -0,0 +1,18 @@ + + + + + + diff --git a/packages/SystemUI/res/drawable/stat_sys_vowifi_slot12.xml b/packages/SystemUI/res/drawable/stat_sys_vowifi_slot12.xml new file mode 100644 index 000000000000..9504b390ff99 --- /dev/null +++ b/packages/SystemUI/res/drawable/stat_sys_vowifi_slot12.xml @@ -0,0 +1,18 @@ + + + + + + diff --git a/packages/SystemUI/res/drawable/stat_sys_vowifi_slot2.xml b/packages/SystemUI/res/drawable/stat_sys_vowifi_slot2.xml new file mode 100644 index 000000000000..e851158d8280 --- /dev/null +++ b/packages/SystemUI/res/drawable/stat_sys_vowifi_slot2.xml @@ -0,0 +1,18 @@ + + + + + + diff --git a/packages/SystemUI/res/drawable/volume_background.xml b/packages/SystemUI/res/drawable/volume_background.xml new file mode 100644 index 000000000000..3716166f7344 --- /dev/null +++ b/packages/SystemUI/res/drawable/volume_background.xml @@ -0,0 +1,22 @@ + + + + + + + diff --git a/packages/SystemUI/res/layout-land/volume_dialog.xml b/packages/SystemUI/res/layout-land/volume_dialog.xml index 3b70dc060e84..b1b00a7c7511 100644 --- a/packages/SystemUI/res/layout-land/volume_dialog.xml +++ b/packages/SystemUI/res/layout-land/volume_dialog.xml @@ -25,14 +25,14 @@ android:background="@android:color/transparent" android:theme="@style/volume_dialog_theme"> - @@ -95,6 +95,24 @@ android:orientation="horizontal"> + + + + + + @@ -147,6 +177,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="bottom | right" - android:layout_marginRight="@dimen/volume_tool_tip_right_margin"/> + android:layout_marginLeft="@dimen/volume_tool_tip_horizontal_margin" + android:layout_marginRight="@dimen/volume_tool_tip_horizontal_margin"/> - \ No newline at end of file + diff --git a/packages/SystemUI/res/layout/combined_qs_header.xml b/packages/SystemUI/res/layout/combined_qs_header.xml index 5dc34b9db594..8dbfc6c674a4 100644 --- a/packages/SystemUI/res/layout/combined_qs_header.xml +++ b/packages/SystemUI/res/layout/combined_qs_header.xml @@ -26,7 +26,7 @@ android:paddingLeft="@dimen/qs_panel_padding" android:paddingRight="@dimen/qs_panel_padding" android:visibility="gone" - android:theme="@style/Theme.SystemUI.QuickSettings.Header" + android:theme="@style/QSHeaderTheme" app:layoutDescription="@xml/combined_qs_header_scene"> - \ No newline at end of file + diff --git a/packages/SystemUI/res/layout/contaminant_dialog.xml b/packages/SystemUI/res/layout/contaminant_dialog.xml index 5f8c30532255..230df6ceb138 100644 --- a/packages/SystemUI/res/layout/contaminant_dialog.xml +++ b/packages/SystemUI/res/layout/contaminant_dialog.xml @@ -32,7 +32,7 @@ android:paddingTop="18dp" android:paddingBottom="18dp" android:textAlignment="center" - android:fontFamily="google-sans-medium" + android:fontFamily="@*android:string/config_headlineFontFamilyMedium" android:textSize="20sp" android:textStyle="bold" android:textColor="?android:attr/textColorPrimary"/> diff --git a/packages/SystemUI/res/layout/current_weather_view.xml b/packages/SystemUI/res/layout/current_weather_view.xml new file mode 100644 index 000000000000..c9ea1de8a0fe --- /dev/null +++ b/packages/SystemUI/res/layout/current_weather_view.xml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + diff --git a/packages/SystemUI/res/layout/keyboard_shortcuts_category_title.xml b/packages/SystemUI/res/layout/keyboard_shortcuts_category_title.xml index 8414223b7654..0d7fdeb00b5b 100644 --- a/packages/SystemUI/res/layout/keyboard_shortcuts_category_title.xml +++ b/packages/SystemUI/res/layout/keyboard_shortcuts_category_title.xml @@ -19,7 +19,7 @@ android:layout_width="wrap_content" android:layout_height="match_parent" android:textSize="14sp" - android:fontFamily="sans-serif-medium" + android:fontFamily="@*android:string/config_bodyFontFamilyMedium" android:paddingStart="24dp" android:paddingTop="20dp" android:paddingEnd="24dp" diff --git a/packages/SystemUI/res/layout/keyguard_bottom_area.xml b/packages/SystemUI/res/layout/keyguard_bottom_area.xml index 8df8c49ee057..e1b4581d018a 100644 --- a/packages/SystemUI/res/layout/keyguard_bottom_area.xml +++ b/packages/SystemUI/res/layout/keyguard_bottom_area.xml @@ -65,7 +65,7 @@ android:layout_width="@dimen/keyguard_affordance_fixed_width" android:layout_gravity="bottom|start" android:scaleType="center" - android:tint="?android:attr/textColorPrimary" + android:tint="@color/keyguard_button_fg_color" android:background="@drawable/keyguard_bottom_affordance_bg" android:layout_marginStart="@dimen/keyguard_affordance_horizontal_offset" android:layout_marginBottom="@dimen/keyguard_affordance_vertical_offset" @@ -77,7 +77,7 @@ android:layout_width="@dimen/keyguard_affordance_fixed_width" android:layout_gravity="bottom|end" android:scaleType="center" - android:tint="?android:attr/textColorPrimary" + android:tint="@color/keyguard_button_fg_color" android:background="@drawable/keyguard_bottom_affordance_bg" android:layout_marginEnd="@dimen/keyguard_affordance_horizontal_offset" android:layout_marginBottom="@dimen/keyguard_affordance_vertical_offset" diff --git a/packages/SystemUI/res/layout/large_screen_shade_header.xml b/packages/SystemUI/res/layout/large_screen_shade_header.xml index 3029a2777fd9..80f8367afbe1 100644 --- a/packages/SystemUI/res/layout/large_screen_shade_header.xml +++ b/packages/SystemUI/res/layout/large_screen_shade_header.xml @@ -25,7 +25,7 @@ android:paddingLeft="@dimen/large_screen_shade_header_left_padding" android:paddingRight="@dimen/qs_panel_padding" android:visibility="gone" - android:theme="@style/Theme.SystemUI.QuickSettings.Header"> + android:theme="@style/QSHeaderTheme"> + + - diff --git a/packages/SystemUI/res/layout/qs_footer_impl.xml b/packages/SystemUI/res/layout/qs_footer_impl.xml index b1d3ed05333b..6b418017c333 100644 --- a/packages/SystemUI/res/layout/qs_footer_impl.xml +++ b/packages/SystemUI/res/layout/qs_footer_impl.xml @@ -38,6 +38,7 @@ android:id="@+id/build" android:layout_width="0dp" android:layout_height="match_parent" + android:paddingStart="4dp" android:paddingEnd="4dp" android:layout_weight="1" android:clickable="true" diff --git a/packages/SystemUI/res/layout/quick_qs_status_icons.xml b/packages/SystemUI/res/layout/quick_qs_status_icons.xml index 542a1c9d22bd..7a370d8cbc48 100644 --- a/packages/SystemUI/res/layout/quick_qs_status_icons.xml +++ b/packages/SystemUI/res/layout/quick_qs_status_icons.xml @@ -24,7 +24,7 @@ android:minHeight="@dimen/qs_header_row_min_height" android:clickable="false" android:focusable="true" - android:theme="@style/Theme.SystemUI.QuickSettings.Header"> + android:theme="@style/QSHeaderTheme"> + + diff --git a/packages/SystemUI/res/layout/quick_status_bar_header_date_privacy.xml b/packages/SystemUI/res/layout/quick_status_bar_header_date_privacy.xml index 60bc3732cde0..b336a84e5716 100644 --- a/packages/SystemUI/res/layout/quick_status_bar_header_date_privacy.xml +++ b/packages/SystemUI/res/layout/quick_status_bar_header_date_privacy.xml @@ -34,7 +34,8 @@ android:layout_height="match_parent" android:minHeight="48dp" android:layout_weight="1" - android:gravity="center_vertical|start" > + android:gravity="center_vertical|start" + android:paddingStart="@dimen/status_bar_padding_start" > + android:gravity="center_vertical|end" + android:paddingEnd="@dimen/status_bar_padding_end" > diff --git a/packages/SystemUI/res/layout/screen_record_dialog.xml b/packages/SystemUI/res/layout/screen_record_dialog.xml index ab38dd2aebba..601c04200220 100644 --- a/packages/SystemUI/res/layout/screen_record_dialog.xml +++ b/packages/SystemUI/res/layout/screen_record_dialog.xml @@ -128,6 +128,144 @@ android:contentDescription="@string/screenrecord_taps_label" style="@style/ScreenRecord.Switch"/> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -173,4 +311,4 @@ - \ No newline at end of file + diff --git a/packages/SystemUI/res/layout/screenrecord_dot.xml b/packages/SystemUI/res/layout/screenrecord_dot.xml new file mode 100644 index 000000000000..1e6babb00c00 --- /dev/null +++ b/packages/SystemUI/res/layout/screenrecord_dot.xml @@ -0,0 +1,16 @@ + + + + + + diff --git a/packages/SystemUI/res/layout/screenshot_static.xml b/packages/SystemUI/res/layout/screenshot_static.xml index 9c027495aa1e..8f1a35a30e4a 100644 --- a/packages/SystemUI/res/layout/screenshot_static.xml +++ b/packages/SystemUI/res/layout/screenshot_static.xml @@ -55,6 +55,8 @@ android:id="@+id/screenshot_share_chip"/> + diff --git a/packages/SystemUI/res/layout/status_bar.xml b/packages/SystemUI/res/layout/status_bar.xml index e281511140c7..f13d29e6fced 100644 --- a/packages/SystemUI/res/layout/status_bar.xml +++ b/packages/SystemUI/res/layout/status_bar.xml @@ -88,6 +88,7 @@ android:layout_height="match_parent" android:textAppearance="@style/TextAppearance.StatusBar.Clock" android:singleLine="true" + android:visibility="gone" android:paddingStart="@dimen/status_bar_left_clock_starting_padding" android:paddingEnd="@dimen/status_bar_left_clock_end_padding" android:gravity="center_vertical|start" @@ -163,10 +164,41 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/SystemUI/res/layout/status_bar_mobile_signal_group.xml b/packages/SystemUI/res/layout/status_bar_mobile_signal_group.xml index 10d49b38ae75..3e19d62d0984 100644 --- a/packages/SystemUI/res/layout/status_bar_mobile_signal_group.xml +++ b/packages/SystemUI/res/layout/status_bar_mobile_signal_group.xml @@ -68,14 +68,16 @@ android:visibility="gone" /> + android:visibility="gone" + android:layout_gravity="center_vertical"/> + - \ No newline at end of file + diff --git a/packages/SystemUI/res/layout/status_bar_wifi_group_inner.xml b/packages/SystemUI/res/layout/status_bar_wifi_group_inner.xml index 0ea0653ab89f..38ee38803b75 100644 --- a/packages/SystemUI/res/layout/status_bar_wifi_group_inner.xml +++ b/packages/SystemUI/res/layout/status_bar_wifi_group_inner.xml @@ -57,6 +57,11 @@ android:id="@+id/wifi_signal" android:layout_height="@dimen/status_bar_wifi_signal_size" android:layout_width="@dimen/status_bar_wifi_signal_size" /> + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/SystemUI/res/layout/tuner_activity.xml b/packages/SystemUI/res/layout/tuner_activity.xml index 83cbf14edd39..1d76f6766535 100644 --- a/packages/SystemUI/res/layout/tuner_activity.xml +++ b/packages/SystemUI/res/layout/tuner_activity.xml @@ -20,16 +20,9 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> - - \ No newline at end of file + diff --git a/packages/SystemUI/res/layout/udfps_view.xml b/packages/SystemUI/res/layout/udfps_view.xml index 257d238f5c54..0fcbfa161ddf 100644 --- a/packages/SystemUI/res/layout/udfps_view.xml +++ b/packages/SystemUI/res/layout/udfps_view.xml @@ -28,4 +28,10 @@ android:layout_width="match_parent" android:layout_height="match_parent"/> + + diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml index 6a192d4b7e05..839b76e464d2 100644 --- a/packages/SystemUI/res/layout/volume_dialog.xml +++ b/packages/SystemUI/res/layout/volume_dialog.xml @@ -25,14 +25,14 @@ android:clipToPadding="false" android:theme="@style/volume_dialog_theme"> - @@ -94,6 +94,24 @@ android:orientation="horizontal"> + + + + + + @@ -146,6 +176,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="bottom | right" - android:layout_marginRight="@dimen/volume_tool_tip_right_margin"/> + android:layout_marginLeft="@dimen/volume_tool_tip_horizontal_margin" + android:layout_marginRight="@dimen/volume_tool_tip_horizontal_margin"/> \ No newline at end of file diff --git a/packages/SystemUI/res/values-land/dimens.xml b/packages/SystemUI/res/values-land/dimens.xml index 9d7b01c8d252..49ef330dcc52 100644 --- a/packages/SystemUI/res/values-land/dimens.xml +++ b/packages/SystemUI/res/values-land/dimens.xml @@ -59,4 +59,5 @@ 348dp @dimen/qqs_layout_margin_top + @dimen/qs_panel_padding_top diff --git a/packages/SystemUI/res/values-night/bootleg_colors.xml b/packages/SystemUI/res/values-night/bootleg_colors.xml new file mode 100644 index 000000000000..c32f9d9311eb --- /dev/null +++ b/packages/SystemUI/res/values-night/bootleg_colors.xml @@ -0,0 +1,18 @@ + + + + + + @android:color/system_neutral1_900 + diff --git a/packages/SystemUI/res/values-night/styles.xml b/packages/SystemUI/res/values-night/styles.xml index 6f871695c491..0246295fd499 100644 --- a/packages/SystemUI/res/values-night/styles.xml +++ b/packages/SystemUI/res/values-night/styles.xml @@ -24,6 +24,18 @@ true + + + + + + + + + + + diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index 37549c927a90..d378f9463eac 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -81,7 +81,7 @@ - internet,bt,flashlight,dnd,alarm,airplane,controls,wallet,rotation,battery,cast,screenrecord,mictoggle,cameratoggle,location,hotspot,inversion,saver,dark,work,night,reverse,reduce_brightness,qr_code_scanner,onehanded,color_correction,dream + internet,bt,flashlight,dnd,alarm,airplane,controls,wallet,rotation,battery,cast,screenrecord,mictoggle,cameratoggle,location,nfc,hotspot,inversion,saver,dark,work,night,reverse,reduce_brightness,qr_code_scanner,onehanded,color_correction,dream,caffeine,smartpixels,cpuinfo,compass,dataswitch,weather @@ -736,15 +736,11 @@ - @*android:string/status_bar_volume - @*android:string/status_bar_alarm_clock @*android:string/status_bar_call_strength - @*android:string/status_bar_volume - @*android:string/status_bar_alarm_clock @*android:string/status_bar_call_strength diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index f7019dcd06ee..24b14ec84501 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -436,7 +436,7 @@ 48dp - 8dp + 8dp 20dp @@ -475,7 +475,7 @@ 0dp - 76dp + 76dp 2dp @@ -557,9 +557,10 @@ 14sp 16dp 6dp - 4dp + 0dp @dimen/footer_actions_height - 80dp + 48dp + 80dp 14sp 36sp @@ -661,7 +662,7 @@ split shade on keyguard--> 68dp - 20dp + 40dp 18dp @@ -978,7 +979,7 @@ 1px - 23px + 65px 0px diff --git a/packages/SystemUI/res/values/flags.xml b/packages/SystemUI/res/values/flags.xml index 49dd574af829..d0fc1abe42c9 100644 --- a/packages/SystemUI/res/values/flags.xml +++ b/packages/SystemUI/res/values/flags.xml @@ -16,19 +16,19 @@ --> - false + true true - false + true false - false + true false diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 53f1227383b7..f68395274fc1 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -221,6 +221,10 @@ Edit screenshot Share screenshot + + Delete + + Delete screenshot Capture more @@ -269,11 +273,15 @@ Stop Share + + Delete Screen recording saved Tap to view + + Screen recording deleted Error deleting screen recording @@ -1245,6 +1253,9 @@ Alarm + + VPN + Wallet diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index ac3eb7e18539..e159ecca103c 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -163,21 +163,21 @@ - @@ -395,6 +395,8 @@ @null + true + @dimen/max_window_blur_radius - @@ -563,7 +565,7 @@ diff --git a/packages/SystemUI/res/xml/status_bar_prefs.xml b/packages/SystemUI/res/xml/status_bar_prefs.xml new file mode 100644 index 000000000000..58e99e0b5046 --- /dev/null +++ b/packages/SystemUI/res/xml/status_bar_prefs.xml @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/SystemUI/res/xml/tuner_prefs.xml b/packages/SystemUI/res/xml/tuner_prefs.xml index 902de23a9e2a..9bf102fa82dd 100644 --- a/packages/SystemUI/res/xml/tuner_prefs.xml +++ b/packages/SystemUI/res/xml/tuner_prefs.xml @@ -18,96 +18,10 @@ xmlns:sysui="http://schemas.android.com/apk/res-auto" android:title="@string/system_ui_tuner"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + android:title="@string/status_bar" + android:fragment="com.android.systemui.tuner.StatusBarTuner" /> mNumbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 0); + public KeyguardPINView(Context context) { this(context, null); } @@ -167,6 +178,32 @@ protected void onFinishInflate() { new View[]{ null, mEcaView, null }}; + + mScramblePin = Settings.Secure.getIntForUser(getContext().getContentResolver(), + Settings.Secure.LOCKSCREEN_PIN_SCRAMBLE_LAYOUT, 0, + UserHandle.USER_CURRENT) == 1; + if (mScramblePin) { + Collections.shuffle(mNumbers); + // get all children who are NumPadKeys + List views = new ArrayList<>(); + + // mView contains all Views that make up the PIN pad; row0 = the entry test field, then + // rows 1-4 contain the buttons. Iterate over all views that make up the buttons in the + // pad + for (int row = 1; row < 5; row++) { + for (int column = 0; column < 3; column++) { + View key = mViews[row][column]; + if (key instanceof NumPadKey) { + views.add((NumPadKey) key); + } + } + } + // reset the digits in the views + for (int i = 0; i < mNumbers.size(); i++) { + NumPadKey view = views.get(i); + view.setDigit(mNumbers.get(i)); + } + } } @Override @@ -205,6 +242,14 @@ public boolean startDisappearAnimation(boolean needsSlowUnlockTransition, return true; } + @Override + protected int getNumberIndex(int number) { + if (mScramblePin) { + return (mNumbers.indexOf(number) + 1) % mNumbers.size(); + } + return super.getNumberIndex(number); + } + @Override public boolean hasOverlappingRendering() { return false; diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java index 1e5c53de4446..2cc5ccdc3fa1 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java @@ -24,7 +24,6 @@ import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_PREPARE_FOR_UPDATE; import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_RESTART; import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TIMEOUT; -import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TRUSTAGENT_EXPIRED; import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_USER_REQUEST; import android.animation.Animator; @@ -107,8 +106,6 @@ protected int getPromptReasonStringRes(int reason) { return R.string.kg_prompt_reason_timeout_password; case PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT: return R.string.kg_prompt_reason_timeout_password; - case PROMPT_REASON_TRUSTAGENT_EXPIRED: - return R.string.kg_prompt_reason_timeout_password; case PROMPT_REASON_NONE: return 0; default: diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java index 29e912fdab32..3e756b5152e8 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java @@ -56,6 +56,7 @@ public class KeyguardPasswordViewController private final InputMethodManager mInputMethodManager; private final DelayableExecutor mMainExecutor; private final KeyguardViewController mKeyguardViewController; + private final LockPatternUtils mLockPatternUtils; private final boolean mShowImeAtScreenOn; private EditText mPasswordEntry; private ImageView mSwitchImeButton; @@ -91,6 +92,10 @@ public void onTextChanged(CharSequence s, int start, int before, int count) { public void afterTextChanged(Editable s) { if (!TextUtils.isEmpty(s)) { onUserInput(); + if (s.length() == mLockPatternUtils.getPinPasswordLength( + KeyguardUpdateMonitor.getCurrentUser())) { + verifyPasswordAndUnlock(); + } } } }; @@ -130,6 +135,7 @@ protected KeyguardPasswordViewController(KeyguardPasswordView view, mShowImeAtScreenOn = resources.getBoolean(R.bool.kg_show_ime_at_screen_on); mPasswordEntry = mView.findViewById(mView.getPasswordTextViewId()); mSwitchImeButton = mView.findViewById(R.id.switch_ime_button); + mLockPatternUtils = lockPatternUtils; } @Override diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java index 5b223242670c..987164557a7a 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java @@ -330,9 +330,6 @@ public void showPromptReason(int reason) { case PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT: mMessageAreaController.setMessage(R.string.kg_prompt_reason_timeout_pattern); break; - case PROMPT_REASON_TRUSTAGENT_EXPIRED: - mMessageAreaController.setMessage(R.string.kg_prompt_reason_timeout_pattern); - break; case PROMPT_REASON_NONE: break; default: diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java index 0a91150e6c39..0ae84ad1a790 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java @@ -22,7 +22,6 @@ import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_PREPARE_FOR_UPDATE; import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_RESTART; import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TIMEOUT; -import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TRUSTAGENT_EXPIRED; import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_USER_REQUEST; import android.animation.Animator; @@ -124,8 +123,6 @@ protected int getPromptReasonStringRes(int reason) { return R.string.kg_prompt_reason_timeout_pin; case PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT: return R.string.kg_prompt_reason_timeout_pin; - case PROMPT_REASON_TRUSTAGENT_EXPIRED: - return R.string.kg_prompt_reason_timeout_pin; case PROMPT_REASON_NONE: return 0; default: @@ -133,9 +130,13 @@ protected int getPromptReasonStringRes(int reason) { } } + protected int getNumberIndex(int number) { + return number; + } + private void performNumberClick(int number) { if (number >= 0 && number <= 9) { - mButtons[number].performClick(); + mButtons[getNumberIndex(number)].performClick(); } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java index 89fcc47caf57..2d758caa25d0 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java @@ -16,7 +16,12 @@ package com.android.keyguard; +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; import android.view.View; +import android.widget.LinearLayout; + +import androidx.constraintlayout.helper.widget.Flow; import com.android.internal.util.LatencyTracker; import com.android.internal.widget.LockPatternUtils; @@ -24,6 +29,12 @@ import com.android.systemui.R; import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.statusbar.policy.DevicePostureController; +import com.android.keyguard.PasswordTextView.QuickUnlockListener; + +import java.util.Arrays; +import java.util.Collections; +import java.util.stream.Collectors; +import java.util.List; public class KeyguardPinViewController extends KeyguardPinBasedInputViewController { @@ -31,6 +42,9 @@ public class KeyguardPinViewController private final DevicePostureController mPostureController; private final DevicePostureController.Callback mPostureCallback = posture -> mView.onDevicePostureChanged(posture); + private final LockPatternUtils mLockPatternUtils; + private final View mDeleteButton; + private boolean mDeleteButtonShowing = true; protected KeyguardPinViewController(KeyguardPINView view, KeyguardUpdateMonitor keyguardUpdateMonitor, @@ -46,12 +60,51 @@ protected KeyguardPinViewController(KeyguardPINView view, emergencyButtonController, falsingCollector); mKeyguardUpdateMonitor = keyguardUpdateMonitor; mPostureController = postureController; + mLockPatternUtils = lockPatternUtils; + mDeleteButton = mView.findViewById(R.id.delete_button); } @Override protected void onViewAttached() { super.onViewAttached(); + int passwordLength = mLockPatternUtils.getPinPasswordLength( + KeyguardUpdateMonitor.getCurrentUser()); + + mPasswordEntry.setQuickUnlockListener(new QuickUnlockListener() { + public void onValidateQuickUnlock(String password) { + if (password != null) { + int length = password.length(); + if (length > 0) { + showDeleteButton(true, true); + } else if (length == 0) { + showDeleteButton(false, true); + } + if (length == passwordLength) { + verifyPasswordAndUnlock(); + } + } + } + }); + + showDeleteButton(false, false); + + View okButton = mView.findViewById(R.id.key_enter); + if (okButton != null) { + /* show okButton only if password length is unset + because quick unlock won't work */ + if (passwordLength != -1) { + okButton.setVisibility(View.INVISIBLE); + Flow flow = (Flow) mView.findViewById(R.id.flow1); + if (flow != null) { + List ids = Arrays.stream(flow.getReferencedIds()) + .boxed().collect(Collectors.toList()); + Collections.swap(ids, 9 /* delete_button */, 11 /* key_enter */); + flow.setReferencedIds(ids.stream().mapToInt(i -> i).toArray()); + } + } + } + View cancelBtn = mView.findViewById(R.id.cancel_button); if (cancelBtn != null) { cancelBtn.setOnClickListener(view -> { @@ -79,6 +132,7 @@ public void reloadColors() { void resetState() { super.resetState(); mMessageAreaController.setMessage(""); + showDeleteButton(false, false); } @Override @@ -92,4 +146,30 @@ public boolean startDisappearAnimation(Runnable finishRunnable) { return mView.startDisappearAnimation( mKeyguardUpdateMonitor.needsSlowUnlockTransition(), finishRunnable); } + + private void showDeleteButton(boolean show, boolean animate) { + int visibility = show ? View.VISIBLE : View.INVISIBLE; + if (mDeleteButton != null && mDeleteButtonShowing != show) { + mDeleteButtonShowing = show; + if (animate) { + mDeleteButton.setAlpha(show ? 0.0f : 1.0f); + mDeleteButton.animate() + .alpha(show ? 1.0f : 0.0f) + .setDuration(show ? 250 : 450) + .setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(Animator animation) { + if (show) mDeleteButton.setVisibility(visibility); + } + + @Override + public void onAnimationEnd(Animator animation) { + if (!show) mDeleteButton.setVisibility(visibility); + } + }); + } else { + mDeleteButton.setVisibility(visibility); + } + } + } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java index 9d0a8acf02b4..ac00e9453c97 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java @@ -60,12 +60,6 @@ public interface KeyguardSecurityView { */ int PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT = 7; - /** - * Some auth is required because the trustagent expired either from timeout or manually by - * the user - */ - int PROMPT_REASON_TRUSTAGENT_EXPIRED = 8; - /** * Reset the view and prepare to take input. This should do things like clearing the * password or pattern and clear error messages. diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java index 65a71664e245..cb6b4eb8dc16 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java @@ -49,6 +49,7 @@ import com.android.settingslib.Utils; import com.android.systemui.R; import com.android.systemui.animation.Interpolators; +import com.android.systemui.keyguard.KeyguardSliceProvider; import com.android.systemui.util.wakelock.KeepAwakeAnimationListener; import java.io.PrintWriter; @@ -75,6 +76,7 @@ public class KeyguardSliceView extends LinearLayout { private int mIconSize; private int mIconSizeWithHeader; + private int mWeatherIconSize; /** * Runnable called whenever the view contents change. */ @@ -162,14 +164,18 @@ Map showSlice(RowContent header, List subItem RowContent rc = (RowContent) subItems.get(i); SliceItem item = rc.getSliceItem(); final Uri itemTag = item.getSlice().getUri(); + final boolean isWeatherSlice = itemTag.toString().equals(KeyguardSliceProvider.KEYGUARD_WEATHER_URI); // Try to reuse the view if already exists in the layout KeyguardSliceTextView button = mRow.findViewWithTag(itemTag); if (button == null) { button = new KeyguardSliceTextView(mContext); + button.setShouldTintDrawable(!isWeatherSlice); button.setTextColor(blendedColor); button.setTag(itemTag); final int viewIndex = i - (mHasHeader ? 1 : 0); mRow.addView(button, viewIndex); + } else { + button.setShouldTintDrawable(!isWeatherSlice); } PendingIntent pendingIntent = null; @@ -195,8 +201,8 @@ Map showSlice(RowContent header, List subItem iconDrawable = ((InsetDrawable) iconDrawable).getDrawable(); } final int width = (int) (iconDrawable.getIntrinsicWidth() - / (float) iconDrawable.getIntrinsicHeight() * iconSize); - iconDrawable.setBounds(0, 0, Math.max(width, 1), iconSize); + / (float) iconDrawable.getIntrinsicHeight() * (isWeatherSlice ? mWeatherIconSize : iconSize)); + iconDrawable.setBounds(0, 0, Math.max(width, 1), (isWeatherSlice ? mWeatherIconSize : iconSize)); } } button.setCompoundDrawablesRelative(iconDrawable, null, null, null); @@ -261,6 +267,7 @@ void setTextColor(@ColorInt int textColor) { void onDensityOrFontScaleChanged() { mIconSize = mContext.getResources().getDimensionPixelSize(R.dimen.widget_icon_size); mIconSizeWithHeader = (int) mContext.getResources().getDimension(R.dimen.header_icon_size); + mWeatherIconSize = mContext.getResources().getDimensionPixelSize(R.dimen.weather_icon_size); for (int i = 0; i < mRow.getChildCount(); i++) { View child = mRow.getChildAt(i); @@ -423,12 +430,17 @@ static class KeyguardSliceTextView extends TextView { @StyleRes private static int sStyleId = R.style.TextAppearance_Keyguard_Secondary; + private boolean shouldTintDrawable = true; KeyguardSliceTextView(Context context) { super(context, null /* attrs */, 0 /* styleAttr */, sStyleId); onDensityOrFontScaleChanged(); setEllipsize(TruncateAt.END); } + public void setShouldTintDrawable(boolean shouldTintDrawable){ + this.shouldTintDrawable = shouldTintDrawable; + } + public void onDensityOrFontScaleChanged() { updatePadding(); } @@ -469,6 +481,9 @@ public void setCompoundDrawablesRelative(Drawable start, Drawable top, Drawable } private void updateDrawableColors() { + if (!shouldTintDrawable){ + return; + } final int color = getCurrentTextColor(); for (Drawable drawable : getCompoundDrawables()) { if (drawable != null) { diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java index 83e23bd52f19..ac2ef01cc182 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java @@ -16,14 +16,21 @@ package com.android.keyguard; +import android.content.ContentResolver; import android.content.Context; +import android.content.res.Resources; +import android.os.UserHandle; +import android.provider.Settings; import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; import android.widget.GridLayout; +import com.android.systemui.Dependency; import com.android.systemui.R; +import com.android.systemui.omni.CurrentWeatherView; import com.android.systemui.statusbar.CrossFadeHelper; +import com.android.systemui.tuner.TunerService; import java.io.PrintWriter; import java.util.Set; @@ -33,14 +40,23 @@ * - keyguard clock * - media player (split shade mode only) */ -public class KeyguardStatusView extends GridLayout { +public class KeyguardStatusView extends GridLayout implements + TunerService.Tunable { private static final boolean DEBUG = KeyguardConstants.DEBUG; private static final String TAG = "KeyguardStatusView"; + private static final String LOCKSCREEN_WEATHER_ENABLED = + "system:" + Settings.System.OMNI_LOCKSCREEN_WEATHER_ENABLED; + private static final String LOCKSCREEN_WEATHER_STYLE = + "system:" + Settings.System.AICP_LOCKSCREEN_WEATHER_STYLE; + private ViewGroup mStatusViewContainer; private KeyguardClockSwitch mClockView; private KeyguardSliceView mKeyguardSlice; private View mMediaHostContainer; + private CurrentWeatherView mWeatherView; + private boolean mShowWeather; + private boolean mOmniStyle; private float mDarkAmount = 0; @@ -54,6 +70,9 @@ public KeyguardStatusView(Context context, AttributeSet attrs) { public KeyguardStatusView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); + Dependency.get(TunerService.class).addTunable(this, + LOCKSCREEN_WEATHER_ENABLED, + LOCKSCREEN_WEATHER_STYLE); } @Override @@ -62,15 +81,18 @@ protected void onFinishInflate() { mStatusViewContainer = findViewById(R.id.status_view_container); mClockView = findViewById(R.id.keyguard_clock_container); - if (KeyguardClockAccessibilityDelegate.isNeeded(mContext)) { - mClockView.setAccessibilityDelegate(new KeyguardClockAccessibilityDelegate(mContext)); - } mKeyguardSlice = findViewById(R.id.keyguard_slice_view); + mWeatherView = (CurrentWeatherView) findViewById(R.id.weather_container); + mMediaHostContainer = findViewById(R.id.status_view_media_container); updateDark(); + updateWeatherView(); + + //mKeyguardSlice.setContentChangeListener(this::onSliceContentChanged); + //onSliceContentChanged(); doc: This is removed with A13 } void setDarkAmount(float darkAmount) { @@ -84,6 +106,9 @@ void setDarkAmount(float darkAmount) { void updateDark() { mKeyguardSlice.setDarkAmount(mDarkAmount); + if (mWeatherView != null) { + mWeatherView.blendARGB(mDarkAmount); + } } /** Sets a translationY value on every child view except for the media view. */ @@ -112,4 +137,34 @@ public void dump(PrintWriter pw, String[] args) { mKeyguardSlice.dump(pw, args); } } + + @Override + public void onTuningChanged(String key, String newValue) { + switch (key) { + case LOCKSCREEN_WEATHER_ENABLED: + mShowWeather = + TunerService.parseIntegerSwitch(newValue, false); + updateWeatherView(); + break; + case LOCKSCREEN_WEATHER_STYLE: + mOmniStyle = + !TunerService.parseIntegerSwitch(newValue, false); + updateWeatherView(); + break; + default: + break; + } + } + + public void updateWeatherView() { + if (mWeatherView != null) { + if (mShowWeather && mOmniStyle && mKeyguardSlice.getVisibility() == View.VISIBLE) { + mWeatherView.setVisibility(View.VISIBLE); + mWeatherView.enableUpdates(); + } else if (!mShowWeather || !mOmniStyle) { + mWeatherView.setVisibility(View.GONE); + mWeatherView.disableUpdates(); + } + } + } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java index c715a4eaef2b..d23a6a27c1f2 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java @@ -203,6 +203,11 @@ public void onLocaleListChanged() { public void onDensityOrFontScaleChanged() { mKeyguardClockSwitchController.onDensityOrFontScaleChanged(); } + + @Override + public void onThemeChanged() { + mKeyguardClockSwitchController.onDensityOrFontScaleChanged(); + } }; private KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() { @@ -216,8 +221,14 @@ public void onKeyguardVisibilityChanged(boolean showing) { if (showing) { if (DEBUG) Slog.v(TAG, "refresh statusview showing:" + showing); refreshTime(); + mView.updateWeatherView(); } } + + @Override + public void onUserSwitchComplete(int userId) { + mView.updateWeatherView(); + } }; /** diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index 6eef3b33cf8f..99d26b9cb874 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -81,6 +81,7 @@ import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; +import android.content.ContentResolver; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.IPackageManager; @@ -112,6 +113,8 @@ import android.os.Trace; import android.os.UserHandle; import android.os.UserManager; +import android.pocket.IPocketCallback; +import android.pocket.PocketManager; import android.provider.Settings; import android.service.dreams.DreamService; import android.service.dreams.IDreamManager; @@ -217,6 +220,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab private static final int MSG_REQUIRE_NFC_UNLOCK = 345; private static final int MSG_KEYGUARD_DISMISS_ANIMATION_FINISHED = 346; + // Additional messages should be 600+ + private static final int MSG_POCKET_STATE_CHANGED = 600; + /** Biometric authentication state: Not listening. */ private static final int BIOMETRIC_STATE_STOPPED = 0; @@ -365,6 +371,8 @@ public void onExpandedChanged(boolean isExpanded) { private final PowerManager mPowerManager; private final boolean mWakeOnFingerprintAcquiredStart; + private final boolean mFingerprintWakeAndUnlock; + /** * Short delay before restarting fingerprint authentication after a successful try. This should * be slightly longer than the time between onFingerprintAuthenticated and @@ -390,6 +398,27 @@ protected Handler getHandler() { } private final Handler mHandler; + private PocketManager mPocketManager; + private boolean mIsDeviceInPocket; + private final IPocketCallback mPocketCallback = new IPocketCallback.Stub() { + @Override + public void onStateChanged(boolean isDeviceInPocket, int reason) { + boolean wasInPocket = mIsDeviceInPocket; + if (reason == PocketManager.REASON_SENSOR) { + mIsDeviceInPocket = isDeviceInPocket; + } else { + mIsDeviceInPocket = false; + } + if (wasInPocket != mIsDeviceInPocket) { + mHandler.sendEmptyMessage(MSG_POCKET_STATE_CHANGED); + } + } + }; + + public boolean isPocketLockVisible(){ + return mPocketManager.isPocketLockVisible(); + } + private SparseBooleanArray mBiometricEnabledForUser = new SparseBooleanArray(); private BiometricManager mBiometricManager; private IBiometricEnabledOnKeyguardCallback mBiometricEnabledCallback = @@ -449,6 +478,13 @@ static class BiometricAuthenticated { private static int sCurrentUser; + SettingsObserver mSettingsObserver; + + private final boolean mFaceAuthOnlyOnSecurityView; + public static final int FACE_UNLOCK_BEHAVIOR_DEFAULT = 0; + public static final int FACE_UNLOCK_BEHAVIOR_SWIPE = 1; + private int mFaceUnlockBehavior = FACE_UNLOCK_BEHAVIOR_DEFAULT; + public synchronized static void setCurrentUser(int currentUser) { sCurrentUser = currentUser; } @@ -828,6 +864,9 @@ private void handleFingerprintAcquired( private void handleFingerprintAuthenticated(int authUserId, boolean isStrongBiometric) { Trace.beginSection("KeyGuardUpdateMonitor#handlerFingerPrintAuthenticated"); + if (mOccludingAppRequestingFace){ + requestFaceAuthOnOccludingApp(false); + } if (mHandler.hasCallbacks(mFpCancelNotReceived)) { mLogger.d("handleFingerprintAuthenticated()" + " triggered while waiting for cancellation, removing watchdog"); @@ -1052,6 +1091,9 @@ private void handleFaceAcquired(int acquireInfo) { private void handleFaceAuthenticated(int authUserId, boolean isStrongBiometric) { Trace.beginSection("KeyGuardUpdateMonitor#handlerFaceAuthenticated"); + if (mOccludingAppRequestingFace){ + requestFaceAuthOnOccludingApp(false); + } try { if (mGoingToSleep) { mLogger.d("Aborted successful auth because device is going to sleep."); @@ -1235,9 +1277,9 @@ private boolean isFaceDisabled(int userId) { final DevicePolicyManager dpm = (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE); // TODO(b/140035044) - return whitelistIpcs(() -> dpm != null && (dpm.getKeyguardDisabledFeatures(null, userId) + return dpm != null && (dpm.getKeyguardDisabledFeatures(null, userId) & DevicePolicyManager.KEYGUARD_DISABLE_FACE) != 0 - || isSimPinSecure()); + || isSimPinSecure(); } /** @@ -1919,6 +1961,10 @@ protected KeyguardUpdateMonitor( mTelephonyListenerManager = telephonyListenerManager; mDeviceProvisioned = isDeviceProvisionedInSettingsDb(); mStrongAuthTracker = new StrongAuthTracker(context, this::notifyStrongAuthStateChanged); + mFingerprintWakeAndUnlock = mContext.getResources().getBoolean( + com.android.systemui.R.bool.config_fingerprintWakeAndUnlock); + mFaceAuthOnlyOnSecurityView = mContext.getResources().getBoolean( + com.android.internal.R.bool.config_faceAuthOnlyOnSecurityView); mBackgroundExecutor = backgroundExecutor; mBroadcastDispatcher = broadcastDispatcher; mInteractionJankMonitor = interactionJankMonitor; @@ -2046,6 +2092,10 @@ public void handleMessage(Message msg) { case MSG_KEYGUARD_DISMISS_ANIMATION_FINISHED: handleKeyguardDismissAnimationFinished(); break; + case MSG_POCKET_STATE_CHANGED: + updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE, + FACE_AUTH_UPDATED_FP_AUTHENTICATED); + break; default: super.handleMessage(msg); break; @@ -2060,7 +2110,8 @@ public void handleMessage(Message msg) { } // Take a guess at initial SIM state, battery status and PLMN until we get an update - mBatteryStatus = new BatteryStatus(BATTERY_STATUS_UNKNOWN, 100, 0, 0, 0, true); + mBatteryStatus = new BatteryStatus(BATTERY_STATUS_UNKNOWN, + 100, 0, 0, 0, true, 0, 0, 0, false, false, false); // Watch for interesting updates final IntentFilter filter = new IntentFilter(); @@ -2110,6 +2161,11 @@ public void handleMessage(Message msg) { mDreamManager = IDreamManager.Stub.asInterface( ServiceManager.getService(DreamService.DREAM_SERVICE)); + mPocketManager = (PocketManager) context.getSystemService(Context.POCKET_SERVICE); + if (mPocketManager != null) { + mPocketManager.addCallback(mPocketCallback); + } + if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) { mFpm = (FingerprintManager) context.getSystemService(Context.FINGERPRINT_SERVICE); mFingerprintSensorProperties = mFpm.getSensorPropertiesInternal(); @@ -2191,6 +2247,8 @@ public void onChange(boolean selfChange) { mContext.getContentResolver().registerContentObserver( Settings.System.getUriFor(Settings.System.TIME_12_24), false, mTimeFormatChangeObserver, UserHandle.USER_ALL); + mSettingsObserver = new SettingsObserver(mHandler); + mSettingsObserver.observe(); } private void updateFaceEnrolled(int userId) { @@ -2508,7 +2566,7 @@ private boolean shouldListenForFaceAssistant() { BiometricAuthenticated face = mUserFaceAuthenticated.get(getCurrentUser()); return mAssistantVisible && mKeyguardOccluded && !(face != null && face.mAuthenticated) - && !mUserHasTrust.get(getCurrentUser(), false); + && !mUserHasTrust.get(getCurrentUser(), false) && !mIsDeviceInPocket; } private boolean shouldTriggerActiveUnlockForAssistant() { @@ -2521,7 +2579,18 @@ protected boolean shouldListenForFingerprint(boolean isUdfps) { final int user = getCurrentUser(); final boolean userDoesNotHaveTrust = !getUserHasTrust(user); final boolean shouldListenForFingerprintAssistant = shouldListenForFingerprintAssistant(); - final boolean shouldListenKeyguardState = + final boolean shouldListenKeyguardState; + if (!mFingerprintWakeAndUnlock) { + shouldListenKeyguardState = + (mKeyguardIsVisible + || mBouncerIsOrWillBeShowing + || shouldListenForFingerprintAssistant + || (mKeyguardOccluded && mIsDreaming)) + && mDeviceInteractive && !mGoingToSleep && !mKeyguardGoingAway + || (mKeyguardOccluded && userDoesNotHaveTrust + && (mOccludingAppRequestingFp || isUdfps)); + } else { + shouldListenKeyguardState = mKeyguardIsVisible || !mDeviceInteractive || (mBouncerIsOrWillBeShowing && !mKeyguardGoingAway) @@ -2530,6 +2599,7 @@ protected boolean shouldListenForFingerprint(boolean isUdfps) { || (mKeyguardOccluded && mIsDreaming) || (mKeyguardOccluded && userDoesNotHaveTrust && (mOccludingAppRequestingFp || isUdfps)); + } // Only listen if this KeyguardUpdateMonitor belongs to the primary user. There is an // instance of KeyguardUpdateMonitor for each user but KeyguardUpdateMonitor is user-aware. @@ -2553,7 +2623,7 @@ protected boolean shouldListenForFingerprint(boolean isUdfps) { && userDoesNotHaveTrust); final boolean shouldListen = shouldListenKeyguardState && shouldListenUserState - && shouldListenBouncerState && shouldListenUdfpsState && !isFingerprintLockedOut(); + && shouldListenBouncerState && shouldListenUdfpsState && !isFingerprintLockedOut() && !mIsDeviceInPocket; maybeLogListenerModelData( new KeyguardFingerprintListenModel( @@ -2638,7 +2708,7 @@ public boolean shouldListenForFace() { // Only listen if this KeyguardUpdateMonitor belongs to the primary user. There is an // instance of KeyguardUpdateMonitor for each user but KeyguardUpdateMonitor is user-aware. - final boolean shouldListen = + boolean shouldListen = (mBouncerFullyShown && !mGoingToSleep || mAuthInterruptActive || mOccludingAppRequestingFace @@ -2653,6 +2723,10 @@ public boolean shouldListenForFace() { && !faceAuthenticated && !fpOrFaceIsLockedOut; + if (shouldListen && mFaceUnlockBehavior == FACE_UNLOCK_BEHAVIOR_SWIPE && !mBouncerFullyShown){ + shouldListen = false; + } + // Aggregate relevant fields for debug logging. maybeLogListenerModelData( new KeyguardFaceListenModel( @@ -2681,6 +2755,10 @@ public boolean shouldListenForFace() { return shouldListen; } + public int getFaceUnlockBehavior() { + return mFaceUnlockBehavior; + } + private void maybeLogListenerModelData(KeyguardListenModel model) { mLogger.logKeyguardListenerModel(model); @@ -2810,7 +2888,7 @@ public boolean getCachedIsUnlockWithFingerprintPossible(int userId) { return mIsUnlockWithFingerprintPossible.getOrDefault(userId, false); } - private boolean isUnlockWithFacePossible(int userId) { + public boolean isUnlockWithFacePossible(int userId) { return isFaceAuthEnabledForUser(userId) && !isFaceDisabled(userId); } @@ -3160,6 +3238,7 @@ public void onKeyguardVisibilityChanged(boolean showing) { Assert.isMainThread(); mLogger.logKeyguardVisibilityChanged(showing); mKeyguardIsVisible = showing; + mBouncerFullyShown = false; if (showing) { mSecureCameraLaunched = false; @@ -3182,6 +3261,7 @@ private void handleKeyguardReset() { mLogger.d("handleKeyguardReset"); updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE, FACE_AUTH_UPDATED_KEYGUARD_RESET); + mBouncerFullyShown = false; mNeedsSlowUnlockTransition = resolveNeedsSlowUnlockTransition(); } @@ -3252,6 +3332,15 @@ private void handleKeyguardBouncerChanged(int bouncerIsOrWillBeShowing, int boun } } + public void updateFaceListeningStateForBehavior(boolean fullyShow) { + if (mBouncerFullyShown != fullyShow){ + mBouncerFullyShown = fullyShow; + if (mFaceUnlockBehavior == FACE_UNLOCK_BEHAVIOR_SWIPE){ + updateFaceListeningState(BIOMETRIC_ACTION_UPDATE, FACE_AUTH_UPDATED_PRIMARY_BOUNCER_SHOWN); + } + } + } + /** * Handle {@link #MSG_REQUIRE_NFC_UNLOCK} */ @@ -3282,6 +3371,7 @@ private void handleKeyguardDismissAnimationFinished() { * Handle {@link #MSG_REPORT_EMERGENCY_CALL_ACTION} */ private void handleReportEmergencyCallAction() { + mBouncerFullyShown = false; Assert.isMainThread(); for (int i = 0; i < mCallbacks.size(); i++) { KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); @@ -3309,8 +3399,16 @@ private boolean isBatteryUpdateInteresting(BatteryStatus old, BatteryStatus curr return true; } + // change in battery temperature + if (old.temperature != current.temperature) { + return true; + } + // change in charging current while plugged in - if (nowPluggedIn && current.maxChargingWattage != old.maxChargingWattage) { + if (nowPluggedIn && + (current.maxChargingWattage != old.maxChargingWattage || + current.maxChargingCurrent != old.maxChargingCurrent || + current.maxChargingVoltage != old.maxChargingVoltage)) { return true; } @@ -3324,6 +3422,21 @@ private boolean isBatteryUpdateInteresting(BatteryStatus old, BatteryStatus curr return true; } + // change in dash charging while plugged in + if (nowPluggedIn && current.dashChargeStatus != old.dashChargeStatus) { + return true; + } + + // change in warp charging while plugged in + if (nowPluggedIn && current.warpChargeStatus != old.warpChargeStatus) { + return true; + } + + // change in VOOC charging while plugged in + if (nowPluggedIn && current.voocChargeStatus != old.voocChargeStatus) { + return true; + } + return false; } @@ -3676,6 +3789,48 @@ protected int getBiometricLockoutDelay() { return BIOMETRIC_LOCKOUT_RESET_DELAY_MS; } + class SettingsObserver extends ContentObserver { + private ContentResolver mContentResolver; + SettingsObserver(Handler handler) { + super(handler); + } + + void observe() { + mContentResolver = mContext.getContentResolver(); + mContentResolver.registerContentObserver(Settings.Secure.getUriFor( + Settings.Secure.FACE_UNLOCK_METHOD), false, this, + UserHandle.USER_ALL); + updateSettings(); + } + + void unobserve(){ + if (mContentResolver != null){ + mContentResolver.unregisterContentObserver(this); + } + } + + @Override + public void onChange(boolean selfChange) { + updateSettings(); + } + } + + private void updateSettings() { + ContentResolver resolver = mContext.getContentResolver(); + updateFaceUnlockBehavior(); + } + + private void updateFaceUnlockBehavior() { + ContentResolver resolver = mContext.getContentResolver(); + if (mFaceAuthOnlyOnSecurityView){ + mFaceUnlockBehavior = FACE_UNLOCK_BEHAVIOR_SWIPE; + }else{ + mFaceUnlockBehavior = Settings.Secure.getIntForUser(resolver, + Settings.Secure.FACE_UNLOCK_METHOD, FACE_UNLOCK_BEHAVIOR_DEFAULT, + UserHandle.USER_CURRENT); + } + } + /** * Unregister all listeners. */ @@ -3697,6 +3852,10 @@ public void destroy() { mContext.getContentResolver().unregisterContentObserver(mTimeFormatChangeObserver); } + if (mSettingsObserver != null) { + mSettingsObserver.unobserve(); + } + try { ActivityManager.getService().unregisterUserSwitchObserver(mUserSwitchObserver); } catch (RemoteException e) { diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconView.java b/packages/SystemUI/src/com/android/keyguard/LockIconView.java index 0a82968ae4cb..efbed4fcde19 100644 --- a/packages/SystemUI/src/com/android/keyguard/LockIconView.java +++ b/packages/SystemUI/src/com/android/keyguard/LockIconView.java @@ -85,7 +85,7 @@ void setDozeAmount(float dozeAmount) { void updateColorAndBackgroundVisibility() { if (mUseBackground && mLockIcon.getDrawable() != null) { mLockIconColor = ColorUtils.blendARGB( - Utils.getColorAttrDefaultColor(getContext(), android.R.attr.textColorPrimary), + getContext().getColor(R.color.keyguard_button_fg_color), Color.WHITE, mDozeAmount); mBgView.setBackground(getContext().getDrawable(R.drawable.fingerprint_bg)); @@ -93,7 +93,7 @@ void updateColorAndBackgroundVisibility() { mBgView.setVisibility(View.VISIBLE); } else { mLockIconColor = ColorUtils.blendARGB( - Utils.getColorAttrDefaultColor(getContext(), R.attr.wallpaperTextColorAccent), + Utils.getColorAttrDefaultColor(getContext(), R.attr.wallpaperTextColor), Color.WHITE, mDozeAmount); mBgView.setVisibility(View.GONE); diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java index 0a4880e1ce66..5db83d20a5cb 100644 --- a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java +++ b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java @@ -22,6 +22,8 @@ import android.graphics.drawable.GradientDrawable; import android.os.PowerManager; import android.os.SystemClock; +import android.os.UserHandle; +import android.provider.Settings; import android.util.AttributeSet; import android.view.HapticFeedbackConstants; import android.view.LayoutInflater; @@ -117,30 +119,42 @@ protected NumPadKey(Context context, AttributeSet attrs, int defStyle, int conte mDigitText.setText(Integer.toString(mDigit)); mKlondikeText = (TextView) findViewById(R.id.klondike_text); + updateText(); + setContentDescription(mDigitText.getText().toString()); + + Drawable background = getBackground(); + if (background instanceof GradientDrawable) { + mAnimator = new NumPadAnimator(context, background.mutate(), + R.style.NumPadKey, mDigitText, null); + } else { + mAnimator = null; + } + } + + public void setDigit(int digit) { + mDigit = digit; + updateText(); + } + + private void updateText() { + boolean scramblePin = Settings.Secure.getIntForUser(getContext().getContentResolver(), + Settings.Secure.LOCKSCREEN_PIN_SCRAMBLE_LAYOUT, 0, + UserHandle.USER_CURRENT) == 1; if (mDigit >= 0) { + mDigitText.setText(Integer.toString(mDigit)); if (sKlondike == null) { sKlondike = getResources().getStringArray(R.array.lockscreen_num_pad_klondike); } if (sKlondike != null && sKlondike.length > mDigit) { String klondike = sKlondike[mDigit]; final int len = klondike.length(); - if (len > 0) { + if (len > 0 || scramblePin) { mKlondikeText.setText(klondike); } else if (mKlondikeText.getVisibility() != View.GONE) { mKlondikeText.setVisibility(View.INVISIBLE); } } } - - setContentDescription(mDigitText.getText().toString()); - - Drawable background = getBackground(); - if (background instanceof GradientDrawable) { - mAnimator = new NumPadAnimator(context, background.mutate(), - R.style.NumPadKey, mDigitText, null); - } else { - mAnimator = null; - } } @Override diff --git a/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java b/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java index b1597144d237..de6ae8fbfbc3 100644 --- a/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java +++ b/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java @@ -102,11 +102,25 @@ public class PasswordTextView extends View { private Interpolator mFastOutSlowInInterpolator; private boolean mShowPassword; private UserActivityListener mUserActivityListener; + protected QuickUnlockListener mQuickUnlockListener; public interface UserActivityListener { void onUserActivity(); } + /* Quick unlock management for PIN view. */ + public interface QuickUnlockListener { + /** + * Validate current password and prepare callback if verified. + * @param password The password string to be verified. + */ + void onValidateQuickUnlock(String password); + } + + public void setQuickUnlockListener(QuickUnlockListener listener) { + mQuickUnlockListener = listener; + } + public PasswordTextView(Context context) { this(context, null); } @@ -265,6 +279,10 @@ public void append(char c) { } userActivity(); sendAccessibilityEventTypeViewTextChanged(textbefore, textbefore.length(), 0, 1); + + if (mQuickUnlockListener != null) { + mQuickUnlockListener.onValidateQuickUnlock(mText); + } } public void setUserActivityListener(UserActivityListener userActivitiListener) { @@ -288,6 +306,9 @@ public void deleteLastChar() { sendAccessibilityEventTypeViewTextChanged(textbefore, textbefore.length() - 1, 1, 0); } userActivity(); + if (mQuickUnlockListener != null) { + mQuickUnlockListener.onValidateQuickUnlock(mText); + } } public String getText() { @@ -353,6 +374,9 @@ public void reset(boolean animated, boolean announce) { if (announce) { sendAccessibilityEventTypeViewTextChanged(textbefore, 0, textbefore.length(), 0); } + if (mQuickUnlockListener != null) { + mQuickUnlockListener.onValidateQuickUnlock(mText); + } } void sendAccessibilityEventTypeViewTextChanged(CharSequence beforeText, int fromIndex, diff --git a/packages/SystemUI/src/com/android/systemui/BootReceiver.java b/packages/SystemUI/src/com/android/systemui/BootReceiver.java new file mode 100644 index 000000000000..768e8eaf7c20 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/BootReceiver.java @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui; + +import android.content.BroadcastReceiver; +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.database.ContentObserver; +import android.os.Handler; +import android.provider.Settings; +import android.util.Log; + +/** + * Performs a number of miscellaneous, non-system-critical actions + * after the system has finished booting. + */ +public class BootReceiver extends BroadcastReceiver { + private static final String TAG = "SystemUIBootReceiver"; + private Handler mHandler = new Handler(); + private SettingsObserver mSettingsObserver; + private Context mContext; + + private class SettingsObserver extends ContentObserver { + SettingsObserver(Handler handler) { + super(handler); + } + + void observe() { + mContext.getContentResolver().registerContentObserver(Settings.Secure.getUriFor( + Settings.Secure.SHOW_CPU_OVERLAY), + false, this); + update(); + } + + @Override + public void onChange(boolean selfChange) { + update(); + } + + public void update() { + Intent cpuinfo = new Intent(mContext, com.android.systemui.CPUInfoService.class); + if (Settings.Secure.getInt(mContext.getContentResolver(), Settings.Secure.SHOW_CPU_OVERLAY, 0) != 0) { + mContext.startService(cpuinfo); + } else { + mContext.stopService(cpuinfo); + } + } + } + + @Override + public void onReceive(final Context context, Intent intent) { + try { + mContext = context; + if (mSettingsObserver == null) { + mSettingsObserver = new SettingsObserver(mHandler); + mSettingsObserver.observe(); + } + + // Start the cpu info overlay, if activated + if (Settings.Secure.getInt(mContext.getContentResolver(), Settings.Secure.SHOW_CPU_OVERLAY, 0) != 0) { + Intent cpuinfo = new Intent(mContext, com.android.systemui.CPUInfoService.class); + mContext.startService(cpuinfo); + } + + } catch (Exception e) { + Log.e(TAG, "Can't start load average service", e); + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/CPUInfoService.java b/packages/SystemUI/src/com/android/systemui/CPUInfoService.java new file mode 100644 index 000000000000..b9972e30dc9d --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/CPUInfoService.java @@ -0,0 +1,437 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui; + +import android.app.Service; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.PixelFormat; +import android.graphics.Rect; +import android.graphics.Typeface; +import android.os.Handler; +import android.os.IBinder; +import android.os.Message; +import android.os.RemoteException; +import android.view.Gravity; +import android.view.View; +import android.view.WindowManager; +import android.util.Log; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; +import java.lang.StringBuffer; +import java.util.Arrays; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class CPUInfoService extends Service { + private View mView; + private Thread mCurCPUThread; + private final String TAG = "CPUInfoService"; + private int mNumCpus = 2; + private String[] mCpu = null; + private String[] mCurrFreq = null; + private String[] mCurrGov = null; + + private int CPU_TEMP_DIVIDER = 1; + private String CPU_TEMP_SENSOR = ""; + private String DISPLAY_CPUS = ""; + private boolean mCpuTempAvail; + + private static final String NUM_OF_CPUS_PATH = "/sys/devices/system/cpu/present"; + private static final String CURRENT_CPU = "/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq"; + private static final String CPU_ROOT = "/sys/devices/system/cpu/cpu"; + private static final String CPU_CUR_TAIL = "/cpufreq/scaling_cur_freq"; + private static final String CPU_GOV_TAIL = "/cpufreq/scaling_governor"; + + private class CPUView extends View { + private Paint mOnlinePaint; + private Paint mOfflinePaint; + private float mAscent; + private int mFH; + private int mMaxWidth; + + private int mNeededWidth; + private int mNeededHeight; + private String mCpuTemp; + + private boolean mDataAvail; + + private Handler mCurCPUHandler = new Handler() { + public void handleMessage(Message msg) { + if(msg.obj==null){ + return; + } + if(msg.what==1){ + String msgData = (String) msg.obj; + try { + String[] parts=msgData.split(";"); + mCpuTemp=parts[0]; + + String[] cpuParts=parts[1].split("\\|"); + for(int i=0; i 1) { + return String.format("%s", + Integer.parseInt(cpuTemp) / CPU_TEMP_DIVIDER); + } else { + return cpuTemp; + } + } + + @Override + public void onDraw(Canvas canvas) { + super.onDraw(canvas); + if (!mDataAvail) { + return; + } + + final int W = mNeededWidth; + final int RIGHT = getWidth()-1; + + int x = RIGHT - mPaddingRight; + int top = mPaddingTop + 2; + int bottom = mPaddingTop + mFH - 2; + + int y = mPaddingTop - (int)mAscent; + + if(!mCpuTemp.equals("0")) { + canvas.drawText("Temp: " + getCpuTemp(mCpuTemp) + "°C", + RIGHT-mPaddingRight-mMaxWidth, y-1, mOnlinePaint); + y += mFH; + } + + for(int i=0; i 0) { + numOfCpu = cpuList.length; + mCpu = new String[numOfCpu]; + + for (int i = 0; i < numOfCpu; i++) { + try { + int cpu = Integer.parseInt(cpuList[i]); + mCpu[i] = cpuList[i]; + } catch (NumberFormatException ex) { + // derped overlay + return getCpus(null); + } + } + } else { + // derped overlay + return getCpus(null); + } + } else { + // empty overlay, take all cores + String numOfCpus = readOneLine(NUM_OF_CPUS_PATH); + cpuList = numOfCpus.split("-"); + if (cpuList.length > 1) { + try { + int cpuStart = Integer.parseInt(cpuList[0]); + int cpuEnd = Integer.parseInt(cpuList[1]); + + numOfCpu = cpuEnd - cpuStart + 1; + + if (numOfCpu < 0) + numOfCpu = 1; + } catch (NumberFormatException ex) { + numOfCpu = 1; + } + } + + mCpu = new String[numOfCpu]; + for (int i = 0; i < numOfCpu; i++) + { + mCpu[i] = String.valueOf(i); + } + } + return numOfCpu; + } + + private BroadcastReceiver mScreenStateReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) { + Log.d(TAG, "ACTION_SCREEN_ON "); + startThread(); + mView.setVisibility(View.VISIBLE); + } else if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) { + Log.d(TAG, "ACTION_SCREEN_OFF"); + mView.setVisibility(View.GONE); + stopThread(); + } + } + }; + + private void startThread() { + Log.d(TAG, "started CurCPUThread"); + mCurCPUThread = new CurCPUThread(mView.getHandler(), mNumCpus); + mCurCPUThread.start(); + } + + private void stopThread() { + if (mCurCPUThread != null && mCurCPUThread.isAlive()) { + Log.d(TAG, "stopping CurCPUThread"); + mCurCPUThread.interrupt(); + try { + mCurCPUThread.join(); + } catch (InterruptedException e) { + } + } + mCurCPUThread = null; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java index a5fdc68226e8..c38398627851 100644 --- a/packages/SystemUI/src/com/android/systemui/Dependency.java +++ b/packages/SystemUI/src/com/android/systemui/Dependency.java @@ -41,6 +41,7 @@ import com.android.systemui.animation.DialogLaunchAnimator; import com.android.systemui.appops.AppOpsController; import com.android.systemui.assist.AssistManager; +import com.android.systemui.biometrics.AuthController; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.dagger.SysUISingleton; @@ -357,6 +358,7 @@ public class Dependency { @Inject Lazy mGroupExpansionManagerLazy; @Inject Lazy mSystemUIDialogManagerLazy; @Inject Lazy mDialogLaunchAnimatorLazy; + @Inject Lazy mAuthController; @Inject public Dependency() { @@ -565,6 +567,8 @@ protected void start() { mProviders.put(SystemUIDialogManager.class, mSystemUIDialogManagerLazy::get); mProviders.put(DialogLaunchAnimator.class, mDialogLaunchAnimatorLazy::get); + mProviders.put(AuthController.class, mAuthController::get); + Dependency.setInstance(this); } diff --git a/packages/SystemUI/src/com/android/systemui/RetickerAnimations.java b/packages/SystemUI/src/com/android/systemui/RetickerAnimations.java new file mode 100644 index 000000000000..80820ffc44e0 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/RetickerAnimations.java @@ -0,0 +1,53 @@ +package com.android.systemui; + +import android.animation.Animator; +import android.animation.ObjectAnimator; +import android.view.animation.BounceInterpolator; +import android.view.View; + +public class RetickerAnimations { + + static boolean mIsAnimatingTicker; + + public static void doBounceAnimationIn(View targetView) { + ObjectAnimator animator = ObjectAnimator.ofFloat(targetView, "translationY", -100, 0, 0); + ObjectAnimator animator2 = ObjectAnimator.ofFloat(targetView, "translationX", 0, 17, 0); + animator.setInterpolator(new BounceInterpolator()); + animator2.setInterpolator(new BounceInterpolator()); + animator.setDuration(500); + animator2.setStartDelay(500); + animator2.setDuration(500); + animator.start(); + animator2.start(); + targetView.setVisibility(View.VISIBLE); + } + + public static void doBounceAnimationOut(View targetView, View notificationStackScroller) { + ObjectAnimator animator = ObjectAnimator.ofFloat(targetView, "translationY", 0, -1000, -1000); + animator.setInterpolator(new BounceInterpolator()); + animator.setDuration(350); + animator.start(); + animator.addListener(new Animator.AnimatorListener() { + @Override + public void onAnimationStart(Animator animation) { + } + @Override + public void onAnimationRepeat(Animator animation) { + } + @Override + public void onAnimationEnd(Animator animation) { + notificationStackScroller.setVisibility(View.VISIBLE); + targetView.setVisibility(View.GONE); + mIsAnimatingTicker = false; + } + @Override + public void onAnimationCancel(Animator animation) { + } + }); + } + + public static boolean isTickerAnimating() { + return mIsAnimatingTicker; + } + +} diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java index 2e13903814a5..c1014bb07afa 100644 --- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java +++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java @@ -606,7 +606,10 @@ private void setupDecorations() { removeHwcOverlay(); } - if (hasOverlays() || hasHwcOverlay()) { + final boolean available = mContext.getResources().getBoolean( + com.android.internal.R.bool.config_displayInversionAvailable); + if (!available) return; + if ((hasOverlays() || hasHwcOverlay())) { if (mIsRegistered) { return; } diff --git a/packages/SystemUI/src/com/android/systemui/VendorServices.java b/packages/SystemUI/src/com/android/systemui/VendorServices.java index 139448c0fab4..a033180217c1 100644 --- a/packages/SystemUI/src/com/android/systemui/VendorServices.java +++ b/packages/SystemUI/src/com/android/systemui/VendorServices.java @@ -18,13 +18,18 @@ import android.content.Context; +import com.android.systemui.smartpixels.SmartPixelsReceiver; + /** * Placeholder for any vendor-specific services. */ public class VendorServices extends CoreStartable { + private SmartPixelsReceiver mSmartPixelsReceiver; + public VendorServices(Context context) { super(context); + mSmartPixelsReceiver = new SmartPixelsReceiver(context); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java index 0ac71c462e21..d0262b40447a 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java @@ -33,6 +33,8 @@ import android.os.Bundle; import android.os.Handler; import android.os.Looper; +import android.os.UserHandle; +import android.provider.Settings; import android.text.TextUtils; import android.text.method.ScrollingMovementMethod; import android.util.AttributeSet; @@ -492,6 +494,12 @@ public void updateState(@BiometricState int newState) { break; case STATE_PENDING_CONFIRMATION: + if (Settings.Secure.getIntForUser(mContext.getContentResolver(), + Settings.Secure.IGNORE_AUTH_CONFIRMATION, + 0, UserHandle.USER_CURRENT) == 1) { + updateState(STATE_AUTHENTICATED); + break; + } removePendingAnimations(); mNegativeButton.setVisibility(View.GONE); mCancelButton.setVisibility(View.VISIBLE); diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java index 8f5cbb76222f..2d87da8c2112 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java @@ -879,7 +879,7 @@ static WindowManager.LayoutParams getLayoutParams(IBinder windowToken, CharSeque final WindowManager.LayoutParams lp = new WindowManager.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, - WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL, + WindowManager.LayoutParams.TYPE_DISPLAY_OVERLAY, windowFlags, PixelFormat.TRANSLUCENT); lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS; diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt index c93fe6ac9f34..e870bd0796ef 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt @@ -28,9 +28,12 @@ import android.util.AttributeSet import android.view.View import android.view.animation.PathInterpolator import com.android.internal.graphics.ColorUtils +import com.android.settingslib.Utils import com.android.systemui.animation.Interpolators import com.android.systemui.ripple.RippleShader +import com.android.systemui.R + private const val RIPPLE_SPARKLE_STRENGTH: Float = 0.4f /** @@ -85,12 +88,12 @@ class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, at } init { - rippleShader.color = 0xffffffff.toInt() // default color + rippleShader.color = Utils.getColorAttrDefaultColor(context, R.attr.wallpaperTextColorAccent) rippleShader.progress = 0f rippleShader.sparkleStrength = RIPPLE_SPARKLE_STRENGTH ripplePaint.shader = rippleShader - dwellShader.color = 0xffffffff.toInt() // default color + dwellShader.color = Utils.getColorAttrDefaultColor(context, R.attr.wallpaperTextColorAccent) dwellShader.progress = 0f dwellShader.distortionStrength = .4f dwellPaint.shader = dwellShader diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/DummyUdfpsDisplayModeProvider.kt b/packages/SystemUI/src/com/android/systemui/biometrics/DummyUdfpsDisplayModeProvider.kt new file mode 100644 index 000000000000..380200983114 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/biometrics/DummyUdfpsDisplayModeProvider.kt @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2022 The LineageOS Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.biometrics + +import android.content.Context +import android.view.Surface + +class DummyUdfpsDisplayModeProvider constructor( + private val context: Context +): UdfpsDisplayModeProvider { + override fun enable(onEnabled: Runnable?) { + onEnabled?.run() + } + + override fun disable(onDisabled: Runnable?) { + onDisabled?.run() + } +} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/PixelUdfpsDisplayModeProvider.kt b/packages/SystemUI/src/com/android/systemui/biometrics/PixelUdfpsDisplayModeProvider.kt new file mode 100644 index 000000000000..01ba3eeab7fb --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/biometrics/PixelUdfpsDisplayModeProvider.kt @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2021 The ProtonAOSP Project + * Copyright (C) 2022 The LineageOS Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.biometrics + +import android.content.Context +import android.hardware.display.DisplayManager +import android.os.IBinder +import android.os.ServiceManager + +import com.android.systemui.biometrics.AuthController +import com.android.systemui.Dependency +import com.google.hardware.pixel.display.IDisplay + +class PixelUdfpsDisplayModeProvider constructor( + private val context: Context +) : UdfpsDisplayModeProvider, IBinder.DeathRecipient, DisplayManager.DisplayListener { + + private val authController = Dependency.get(AuthController::class.java) + private val bgExecutor = Dependency.get(Dependency.BACKGROUND_EXECUTOR) + private val handler = Dependency.get(Dependency.MAIN_HANDLER) + private val displayId = context.getDisplayId() + private val displayManager = context.getSystemService(DisplayManager::class.java) + + private var displayHal = ServiceManager.waitForDeclaredService(PIXEL_DISPLAY_HAL) + .let { binder -> + binder.linkToDeath(this, 0) + IDisplay.Stub.asInterface(binder) + } + + private val peakRefreshRate = displayManager.getDisplay(displayId).supportedModes + .maxOf { it.refreshRate } + private val currentRefreshRate: Float + get() = displayManager.getDisplay(displayId).refreshRate + + // Used by both main and UI background threads + @Volatile private var pendingEnable = false + @Volatile private var pendingEnableCallback: Runnable? = null + @Volatile private var alreadyDisabled = true + + init { + // Listen for refresh rate changes + displayManager.registerDisplayListener(this, handler) + } + + override fun enable(onEnabled: Runnable?) { + // Run the callback and skip enabling if already enabled + // (otherwise it may fail, similar to disabling) + if (displayHal.getLhbmState()) { + onEnabled?.run() + return + } + + // Takes 20-30 ms, so switch to background + bgExecutor.execute { + // Request HbmSVManager to lock the refresh rate. On the Pixel 6 Pro (raven), LHBM only + // works at peak refresh rate. + authController.udfpsHbmListener?.onHbmEnabled(displayId) + + if (currentRefreshRate == peakRefreshRate) { + // Enable immediately if refresh rate is correct + doPendingEnable(onEnabled) + } else { + // Otherwise, queue it and wait for the refresh rate update callback + pendingEnable = true + pendingEnableCallback = onEnabled + } + } + } + + private fun doPendingEnable(callback: Runnable? = null) { + alreadyDisabled = false + displayHal?.setLhbmState(true) + // Make sure callback runs on main thread + (callback ?: pendingEnableCallback)?.let { handler.post(it) } + + pendingEnable = false + pendingEnableCallback = null // to avoid leaking memory + } + + override fun disable(onDisabled: Runnable?) { + // If there's a pending enable, clear it and skip the disable request entirely. + // Otherwise, HBM will be disabled before the enable - while it's already disabled, which + // causes the display HAL call to throw an exception. + if (pendingEnable) { + pendingEnable = false + pendingEnableCallback = null + return + } + + // Also bail out if HBM is already disabled *and* no enable is pending. + // This can happen sometimes if the user spams taps on the UDFPS icon. + if (!displayHal.getLhbmState() && alreadyDisabled) { + return + } + + alreadyDisabled = true + + // Takes 10-20 ms, so switch to background + bgExecutor.execute { + displayHal?.setLhbmState(false) + // Unlock refresh rate + handler.post { authController.udfpsHbmListener?.onHbmDisabled(displayId) } + + onDisabled?.let { handler.post(it) } + } + } + + override fun onDisplayAdded(displayId: Int) = Unit + override fun onDisplayRemoved(displayId: Int) = Unit + override fun onDisplayChanged(displayId: Int) { + // Dispatch pending enable if we were waiting for the refresh rate to change + if (pendingEnable && displayId == this.displayId && currentRefreshRate == peakRefreshRate) { + doPendingEnable() + } + } + + override fun binderDied() { + displayHal = null + } + + companion object { + // Descriptor for Pixel display HAL's AIDL service + private const val PIXEL_DISPLAY_HAL = "com.google.hardware.pixel.display.IDisplay/default" + } +} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.kt index d03106b4e6bc..bba70235d738 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.kt @@ -32,6 +32,7 @@ import android.hardware.fingerprint.FingerprintManager import android.hardware.fingerprint.FingerprintSensorPropertiesInternal import android.hardware.fingerprint.ISidefpsController import android.os.Handler +import android.util.DisplayUtils import android.util.Log import android.util.RotationUtils import android.view.Display @@ -220,12 +221,14 @@ class SidefpsController @Inject constructor( val displayHeight = if (isNaturalOrientation) size.height() else size.width() val boundsWidth = if (isNaturalOrientation) bounds.width() else bounds.height() val boundsHeight = if (isNaturalOrientation) bounds.height() else bounds.width() + val scaleFactor = DisplayUtils.getScaleFactor(context) + val locationY = (scaleFactor * overlayOffsets.sensorLocationY).toInt() val sensorBounds = if (overlayOffsets.isYAligned()) { Rect( displayWidth - boundsWidth, - overlayOffsets.sensorLocationY, + locationY, displayWidth, - overlayOffsets.sensorLocationY + boundsHeight + locationY + boundsHeight ) } else { Rect( diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java index 412dc0577876..505a22d790f5 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java @@ -17,6 +17,7 @@ package com.android.systemui.biometrics; import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_GOOD; +import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_VENDOR; import static android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_KEYGUARD; import static com.android.internal.util.Preconditions.checkNotNull; @@ -29,18 +30,23 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.database.ContentObserver; import android.graphics.Point; import android.hardware.biometrics.BiometricFingerprintConstants; +import android.hardware.display.ColorDisplayManager; import android.hardware.display.DisplayManager; import android.hardware.fingerprint.FingerprintManager; import android.hardware.fingerprint.IUdfpsOverlayController; import android.hardware.fingerprint.IUdfpsOverlayControllerCallback; +import android.net.Uri; import android.os.Handler; import android.os.PowerManager; import android.os.Process; import android.os.Trace; +import android.os.UserHandle; import android.os.VibrationAttributes; import android.os.VibrationEffect; +import android.provider.Settings; import android.util.Log; import android.util.RotationUtils; import android.view.LayoutInflater; @@ -56,6 +62,7 @@ import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.animation.ActivityLaunchAnimator; import com.android.systemui.biometrics.dagger.BiometricsBackground; +import com.android.systemui.biometrics.UdfpsControllerOverlay; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.doze.DozeReceiver; @@ -69,12 +76,16 @@ import com.android.systemui.statusbar.phone.SystemUIDialogManager; import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController; import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager; +import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.concurrency.DelayableExecutor; import com.android.systemui.util.concurrency.Execution; +import com.android.systemui.util.settings.SecureSettings; import com.android.systemui.util.time.SystemClock; +import com.android.systemui.R; + import java.util.HashSet; import java.util.Optional; import java.util.Set; @@ -98,12 +109,15 @@ @SuppressWarnings("deprecation") @SysUISingleton public class UdfpsController implements DozeReceiver { + private static final String TAG = "UdfpsController"; private static final long AOD_INTERRUPT_TIMEOUT_MILLIS = 1000; // Minimum required delay between consecutive touch logs in milliseconds. private static final long MIN_TOUCH_LOG_INTERVAL = 50; + private static final String PULSE_ACTION = "com.android.systemui.doze.pulse"; + private final Context mContext; private final Execution mExecution; private final FingerprintManager mFingerprintManager; @@ -165,6 +179,18 @@ public class UdfpsController implements DozeReceiver { private boolean mOnFingerDown; private boolean mAttemptedToDismissKeyguard; private final Set mCallbacks = new HashSet<>(); + private final int mUdfpsVendorCode; + private final SecureSettings mSecureSettings; + private boolean mScreenOffUdfpsEnabled; + + private boolean mLowPowerMode = false; + private boolean mSmartPixelsFlag = false; + private int mSmartPixelsEnabled = 0; + private int mSmartPixelsOnPowerSave= 0; + private final BatteryController mBatteryController; + + private boolean mFrameworkDimming; + private int[][] mBrightnessAlphaArray; @VisibleForTesting public static final VibrationAttributes UDFPS_VIBRATION_ATTRIBUTES = @@ -185,6 +211,7 @@ public class UdfpsController implements DozeReceiver { private final ScreenLifecycle.Observer mScreenObserver = new ScreenLifecycle.Observer() { @Override public void onScreenTurnedOn() { + isSmartPixelsEnabled(); mScreenOn = true; if (mAodInterruptRunnable != null) { mAodInterruptRunnable.run(); @@ -232,7 +259,7 @@ public void hideUdfpsOverlay(int sensorId) { @Override public void onAcquired( int sensorId, - @BiometricFingerprintConstants.FingerprintAcquired int acquiredInfo + @BiometricFingerprintConstants.FingerprintAcquired int acquiredInfo, int vendorCode ) { if (BiometricFingerprintConstants.shouldDisableUdfpsDisplayMode(acquiredInfo)) { boolean acquiredGood = acquiredInfo == FINGERPRINT_ACQUIRED_GOOD; @@ -251,6 +278,22 @@ public void onAcquired( mOverlay.onAcquiredGood(); } }); + } else { + boolean acquiredVendor = acquiredInfo == FINGERPRINT_ACQUIRED_VENDOR; + final boolean isDozing = mStatusBarStateController.isDozing() || !mScreenOn; + if (acquiredVendor && vendorCode == mUdfpsVendorCode) { + if ((mScreenOffUdfpsEnabled && isDozing) /** Screen off and dozing */ || + (mKeyguardUpdateMonitor.isDreaming() && mScreenOn) /** AOD or pulse */) { + if (mContext.getResources().getBoolean(R.bool.config_pulseOnFingerDown)) { + mContext.sendBroadcastAsUser( + new Intent(PULSE_ACTION), new UserHandle(UserHandle.USER_CURRENT)); + } else { + mPowerManager.wakeUp(mSystemClock.uptimeMillis(), + PowerManager.WAKE_REASON_GESTURE, TAG); + } + onAodInterrupt(0, 0, 0, 0); + } + } } } @@ -447,7 +490,12 @@ boolean onTouch(long requestId, @NonNull MotionEvent event, boolean fromUdfpsVie // We need to persist its ID to track it during ACTION_MOVE that could include // data for many other pointers because of multi-touch support. mActivePointerId = event.getPointerId(0); + final int idx = mActivePointerId == -1 + ? event.getPointerId(0) + : event.findPointerIndex(mActivePointerId); mVelocityTracker.addMovement(event); + onFingerDown(requestId, (int) event.getRawX(), (int) event.getRawY(), + (int) event.getTouchMinor(idx), (int) event.getTouchMajor(idx)); handled = true; mAcquiredReceived = false; } @@ -594,7 +642,7 @@ public UdfpsController(@NonNull Context context, @NonNull VibratorHelper vibrator, @NonNull UdfpsHapticsSimulator udfpsHapticsSimulator, @NonNull UdfpsShell udfpsShell, - @NonNull Optional udfpsDisplayMode, + @NonNull UdfpsDisplayModeProvider udfpsDisplayMode, @NonNull KeyguardStateController keyguardStateController, @NonNull DisplayManager displayManager, @Main Handler mainHandler, @@ -605,7 +653,9 @@ public UdfpsController(@NonNull Context context, @NonNull LatencyTracker latencyTracker, @NonNull ActivityLaunchAnimator activityLaunchAnimator, @NonNull Optional alternateTouchProvider, - @BiometricsBackground Executor biometricsExecutor) { + @NonNull SecureSettings secureSettings, + @BiometricsBackground Executor biometricsExecutor, + @NonNull BatteryController batteryController) { mContext = context; mExecution = execution; mVibrator = vibrator; @@ -626,7 +676,7 @@ public UdfpsController(@NonNull Context context, mPowerManager = powerManager; mAccessibilityManager = accessibilityManager; mLockscreenShadeTransitionController = lockscreenShadeTransitionController; - mUdfpsDisplayMode = udfpsDisplayMode.orElse(null); + mUdfpsDisplayMode = udfpsDisplayMode; screenLifecycle.addObserver(mScreenObserver); mScreenOn = screenLifecycle.getScreenState() == ScreenLifecycle.SCREEN_ON; mConfigurationController = configurationController; @@ -636,6 +686,7 @@ public UdfpsController(@NonNull Context context, mActivityLaunchAnimator = activityLaunchAnimator; mAlternateTouchProvider = alternateTouchProvider.orElse(null); mBiometricExecutor = biometricsExecutor; + mBatteryController = batteryController; mOrientationListener = new BiometricDisplayListener( context, @@ -659,6 +710,80 @@ public UdfpsController(@NonNull Context context, udfpsHapticsSimulator.setUdfpsController(this); udfpsShell.setUdfpsOverlayController(mUdfpsOverlayController); + mUdfpsVendorCode = mContext.getResources().getInteger(R.integer.config_udfpsVendorCode); + mSecureSettings = secureSettings; + updateScreenOffUdfpsState(); + mSecureSettings.registerContentObserver(Settings.Secure.SCREEN_OFF_UDFPS_ENABLED, + new ContentObserver(mainHandler) { + @Override + public void onChange(boolean selfChange, Uri uri) { + if (uri.getLastPathSegment().equals(Settings.Secure.SCREEN_OFF_UDFPS_ENABLED)) { + updateScreenOffUdfpsState(); + } + } + } + ); + } + + private void updateScreenOffUdfpsState() { + boolean isSupported = mContext.getResources().getBoolean( + com.android.internal.R.bool.config_supportsScreenOffUdfps); + mScreenOffUdfpsEnabled = isSupported && mSecureSettings.getInt(Settings.Secure.SCREEN_OFF_UDFPS_ENABLED, 0) == 1; + } + + private void isSmartPixelsEnabled() { + if (!mSmartPixelsFlag) { + mSmartPixelsEnabled = Settings.System.getIntForUser( + mContext.getContentResolver(), Settings.System.SMART_PIXELS_ENABLE, + 0, UserHandle.USER_CURRENT); + Log.i(TAG, "SmartPixels: SmartPixels enabled - " + mSmartPixelsEnabled); + mSmartPixelsOnPowerSave = Settings.System.getIntForUser( + mContext.getContentResolver(), Settings.System.SMART_PIXELS_ON_POWER_SAVE, + 0, UserHandle.USER_CURRENT); + Log.i(TAG, "SmartPixels: SmartPixels on Power Save enabled - " + mSmartPixelsOnPowerSave); + mLowPowerMode = mBatteryController.isPowerSave(); + Log.i(TAG, "SmartPixels: Low power mode - " + mLowPowerMode); + } + } + + private void disableSmartPixels() { + Log.i(TAG, "SmartPixels: Disable SmartPixels"); + if (mLowPowerMode && (mSmartPixelsOnPowerSave == 1) && (mSmartPixelsEnabled == 1)) { + Settings.System.putIntForUser(mContext.getContentResolver(), + Settings.System.SMART_PIXELS_ON_POWER_SAVE, + 0, UserHandle.USER_CURRENT); + Settings.System.putIntForUser(mContext.getContentResolver(), + Settings.System.SMART_PIXELS_ENABLE, + 0, UserHandle.USER_CURRENT); + } else if (mLowPowerMode && (mSmartPixelsOnPowerSave == 1)) { + Settings.System.putIntForUser(mContext.getContentResolver(), + Settings.System.SMART_PIXELS_ON_POWER_SAVE, + 0, UserHandle.USER_CURRENT); + } else { + Settings.System.putIntForUser(mContext.getContentResolver(), + Settings.System.SMART_PIXELS_ENABLE, + 0, UserHandle.USER_CURRENT); + } + } + + private void enableSmartPixels() { + Log.i(TAG, "SmartPixels: Enable SmartPixels"); + if (mLowPowerMode && (mSmartPixelsOnPowerSave == 1) && (mSmartPixelsEnabled == 1)) { + Settings.System.putIntForUser(mContext.getContentResolver(), + Settings.System.SMART_PIXELS_ON_POWER_SAVE, + 1, UserHandle.USER_CURRENT); + Settings.System.putIntForUser(mContext.getContentResolver(), + Settings.System.SMART_PIXELS_ENABLE, + 1, UserHandle.USER_CURRENT); + } else if (mLowPowerMode && (mSmartPixelsOnPowerSave == 1)) { + Settings.System.putIntForUser(mContext.getContentResolver(), + Settings.System.SMART_PIXELS_ON_POWER_SAVE, + 1, UserHandle.USER_CURRENT); + } else { + Settings.System.putIntForUser(mContext.getContentResolver(), + Settings.System.SMART_PIXELS_ENABLE, + 1, UserHandle.USER_CURRENT); + } } /** @@ -696,6 +821,8 @@ private void redrawOverlay() { private void showUdfpsOverlay(@NonNull UdfpsControllerOverlay overlay) { mExecution.assertIsMainThread(); + mFrameworkDimming = mContext.getResources().getBoolean(R.bool.config_udfpsFrameworkDimming); + parseBrightnessAlphaArray(); mOverlay = overlay; final int requestReason = overlay.getRequestReason(); @@ -830,10 +957,20 @@ public boolean isFingerDown() { private void onFingerDown(long requestId, int x, int y, float minor, float major) { mExecution.assertIsMainThread(); + if (!mSmartPixelsFlag) { + if ((mSmartPixelsEnabled == 1) || ((mSmartPixelsOnPowerSave == 1) && mLowPowerMode)) { + disableSmartPixels(); + } + mSmartPixelsFlag = true; + } + if (mOverlay == null) { Log.w(TAG, "Null request in onFingerDown"); return; } + + updateViewDimAmount(true); + if (!mOverlay.matchesRequestId(requestId)) { Log.w(TAG, "Mismatched fingerDown: " + requestId + " current: " + mOverlay.getRequestId()); @@ -891,6 +1028,12 @@ private void onFingerUp(long requestId, @NonNull UdfpsView view) { mExecution.assertIsMainThread(); mActivePointerId = -1; mAcquiredReceived = false; + + if ((mSmartPixelsEnabled == 1) || ((mSmartPixelsOnPowerSave == 1) && mLowPowerMode)) { + enableSmartPixels(); + } + mSmartPixelsFlag = false; + if (mOnFingerDown) { if (mAlternateTouchProvider != null) { mBiometricExecutor.execute(() -> { @@ -918,6 +1061,52 @@ private void onFingerUp(long requestId, @NonNull UdfpsView view) { mCancelAodTimeoutAction = null; } mIsAodInterruptActive = false; + updateViewDimAmount(false); + } + + private static int interpolate(int x, int xa, int xb, int ya, int yb) { + return ya - (ya - yb) * (x - xa) / (xb - xa); + } + + private void updateViewDimAmount(boolean pressed) { + if (mFrameworkDimming) { + if (pressed) { + int curBrightness = Settings.System.getInt(mContext.getContentResolver(), + Settings.System.SCREEN_BRIGHTNESS, 100); + int i, dimAmount; + for (i = 0; i < mBrightnessAlphaArray.length; i++) { + if (mBrightnessAlphaArray[i][0] >= curBrightness) break; + } + if (i == 0) { + dimAmount = mBrightnessAlphaArray[i][1]; + } else if (i == mBrightnessAlphaArray.length) { + dimAmount = mBrightnessAlphaArray[i-1][1]; + } else { + dimAmount = interpolate(curBrightness, + mBrightnessAlphaArray[i][0], mBrightnessAlphaArray[i-1][0], + mBrightnessAlphaArray[i][1], mBrightnessAlphaArray[i-1][1]); + } + // Call the function in UdfpsOverlayController with dimAmount + mOverlay.updateDimAmount(dimAmount / 255.0f); + } else { + // Call the function in UdfpsOverlayController + mOverlay.updateDimAmount(0.0f); + } + } + } + + private void parseBrightnessAlphaArray() { + mFrameworkDimming = mContext.getResources().getBoolean(R.bool.config_udfpsFrameworkDimming); + if (mFrameworkDimming) { + String[] array = mContext.getResources().getStringArray( + R.array.config_udfpsDimmingBrightnessAlphaArray); + mBrightnessAlphaArray = new int[array.length][2]; + for (int i = 0; i < array.length; i++) { + String[] s = array[i].split(","); + mBrightnessAlphaArray[i][0] = Integer.parseInt(s[0]); + mBrightnessAlphaArray[i][1] = Integer.parseInt(s[1]); + } + } } /** diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt index 1c62f8a4e508..f4c3665a195c 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt @@ -93,7 +93,7 @@ class UdfpsControllerOverlay( private var overlayTouchListener: TouchExplorationStateChangeListener? = null private val coreLayoutParams = WindowManager.LayoutParams( - WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG, + WindowManager.LayoutParams.TYPE_DISPLAY_OVERLAY, 0 /* flags set in computeLayoutParams() */, PixelFormat.TRANSLUCENT ).apply { @@ -102,12 +102,21 @@ class UdfpsControllerOverlay( gravity = android.view.Gravity.TOP or android.view.Gravity.LEFT layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS flags = (Utils.FINGERPRINT_OVERLAY_LAYOUT_PARAM_FLAGS or + WindowManager.LayoutParams.FLAG_DIM_BEHIND or WindowManager.LayoutParams.FLAG_SPLIT_TOUCH) privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY + dimAmount = 0.0f + // Avoid announcing window title. accessibilityTitle = " " } + fun updateDimAmount(newDimAmount: Float): Float { + coreLayoutParams.dimAmount = newDimAmount + windowManager.updateViewLayout(overlayView, coreLayoutParams) + return newDimAmount + } + /** A helper if the [requestReason] was due to enrollment. */ val enrollHelper: UdfpsEnrollHelper? = if (requestReason.isEnrollmentReason()) { UdfpsEnrollHelper(context, fingerprintManager, requestReason) diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapter.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapter.java index 43745bf74aae..59a9ad6124f0 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapter.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapter.java @@ -107,10 +107,9 @@ private AuthDialog.LayoutParams onMeasureInternalPortrait(int width, int height, final int buttonBarHeight = getViewHeightPx(R.id.button_bar); final int dialogMargin = getDialogMarginPx(); final int displayHeight = getMaximumWindowBounds(windowMetrics).height(); - final Insets navbarInsets = getNavbarInsets(windowMetrics); mBottomSpacerHeight = calculateBottomSpacerHeightForPortrait( mSensorProps, displayHeight, textIndicatorHeight, buttonBarHeight, - dialogMargin, navbarInsets.bottom, scaleFactor); + dialogMargin, scaleFactor); // Go through each of the children and do the custom measurement. int totalHeight = 0; @@ -197,17 +196,15 @@ private AuthDialog.LayoutParams onMeasureInternalLandscape(int width, int height final int textIndicatorHeight = getViewHeightPx(R.id.indicator); final int buttonBarHeight = getViewHeightPx(R.id.button_bar); - final Insets navbarInsets = getNavbarInsets(windowMetrics); final int bottomSpacerHeight = calculateBottomSpacerHeightForLandscape(titleHeight, subtitleHeight, descriptionHeight, topSpacerHeight, textIndicatorHeight, - buttonBarHeight, navbarInsets.bottom); + buttonBarHeight); // Find the spacer width needed to horizontally align the icon with the sensor. final int displayWidth = getMaximumWindowBounds(windowMetrics).width(); final int dialogMargin = getDialogMarginPx(); - final int horizontalInset = navbarInsets.left + navbarInsets.right; final int horizontalSpacerWidth = calculateHorizontalSpacerWidthForLandscape( - mSensorProps, displayWidth, dialogMargin, horizontalInset, scaleFactor); + mSensorProps, displayWidth, dialogMargin, scaleFactor); final int sensorDiameter = getSensorDiameter(scaleFactor); final int remeasuredWidth = sensorDiameter + 2 * horizontalSpacerWidth; @@ -271,13 +268,6 @@ private int getDialogMarginPx() { return mView.getResources().getDimensionPixelSize(R.dimen.biometric_dialog_border_padding); } - @NonNull - private static Insets getNavbarInsets(@Nullable WindowMetrics windowMetrics) { - return windowMetrics != null - ? windowMetrics.getWindowInsets().getInsets(WindowInsets.Type.navigationBars()) - : Insets.NONE; - } - @NonNull private static Rect getMaximumWindowBounds(@Nullable WindowMetrics windowMetrics) { return windowMetrics != null ? windowMetrics.getBounds() : new Rect(); @@ -291,7 +281,7 @@ private static Rect getMaximumWindowBounds(@Nullable WindowMetrics windowMetrics static int calculateBottomSpacerHeightForPortrait( @NonNull FingerprintSensorPropertiesInternal sensorProperties, int displayHeightPx, int textIndicatorHeightPx, int buttonBarHeightPx, int dialogMarginPx, - int navbarBottomInsetPx, float scaleFactor) { + float scaleFactor) { final SensorLocationInternal location = sensorProperties.getLocation(); final int sensorDistanceFromBottom = displayHeightPx - (int) (scaleFactor * location.sensorLocationY) @@ -300,14 +290,12 @@ static int calculateBottomSpacerHeightForPortrait( final int spacerHeight = sensorDistanceFromBottom - textIndicatorHeightPx - buttonBarHeightPx - - dialogMarginPx - - navbarBottomInsetPx; + - dialogMarginPx; if (DEBUG) { Log.d(TAG, "Display height: " + displayHeightPx + ", Distance from bottom: " + sensorDistanceFromBottom + ", Bottom margin: " + dialogMarginPx - + ", Navbar bottom inset: " + navbarBottomInsetPx + ", Bottom spacer height (portrait): " + spacerHeight + ", Scale Factor: " + scaleFactor); } @@ -322,7 +310,7 @@ static int calculateBottomSpacerHeightForPortrait( @VisibleForTesting static int calculateBottomSpacerHeightForLandscape(int titleHeightPx, int subtitleHeightPx, int descriptionHeightPx, int topSpacerHeightPx, int textIndicatorHeightPx, - int buttonBarHeightPx, int navbarBottomInsetPx) { + int buttonBarHeightPx) { final int dialogHeightAboveIcon = titleHeightPx + subtitleHeightPx @@ -332,8 +320,7 @@ static int calculateBottomSpacerHeightForLandscape(int titleHeightPx, int subtit final int dialogHeightBelowIcon = textIndicatorHeightPx + buttonBarHeightPx; final int bottomSpacerHeight = dialogHeightAboveIcon - - dialogHeightBelowIcon - - navbarBottomInsetPx; + - dialogHeightBelowIcon; if (DEBUG) { Log.d(TAG, "Title height: " + titleHeightPx @@ -342,7 +329,6 @@ static int calculateBottomSpacerHeightForLandscape(int titleHeightPx, int subtit + ", Top spacer height: " + topSpacerHeightPx + ", Text indicator height: " + textIndicatorHeightPx + ", Button bar height: " + buttonBarHeightPx - + ", Navbar bottom inset: " + navbarBottomInsetPx + ", Bottom spacer height (landscape): " + bottomSpacerHeight); } @@ -357,21 +343,19 @@ static int calculateBottomSpacerHeightForLandscape(int titleHeightPx, int subtit @VisibleForTesting static int calculateHorizontalSpacerWidthForLandscape( @NonNull FingerprintSensorPropertiesInternal sensorProperties, int displayWidthPx, - int dialogMarginPx, int navbarHorizontalInsetPx, float scaleFactor) { + int dialogMarginPx, float scaleFactor) { final SensorLocationInternal location = sensorProperties.getLocation(); final int sensorDistanceFromEdge = displayWidthPx - (int) (scaleFactor * location.sensorLocationY) - (int) (scaleFactor * location.sensorRadius); final int horizontalPadding = sensorDistanceFromEdge - - dialogMarginPx - - navbarHorizontalInsetPx; + - dialogMarginPx; if (DEBUG) { Log.d(TAG, "Display width: " + displayWidthPx + ", Distance from edge: " + sensorDistanceFromEdge + ", Dialog margin: " + dialogMarginPx - + ", Navbar horizontal inset: " + navbarHorizontalInsetPx + ", Horizontal spacer width (landscape): " + horizontalPadding + ", Scale Factor: " + scaleFactor); } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java index bc274a0af95f..4701c177dcf7 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java @@ -173,9 +173,8 @@ void updateColor() { return; } - mTextColorPrimary = Utils.getColorAttrDefaultColor(mContext, - android.R.attr.textColorPrimary); mBgProtection.setImageDrawable(getContext().getDrawable(R.drawable.fingerprint_bg)); + mTextColorPrimary = getContext().getColor(R.color.keyguard_button_fg_color); mLockScreenFp.invalidate(); // updated with a valueCallback } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsSurfaceView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsSurfaceView.java new file mode 100644 index 000000000000..a99527acb8bd --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsSurfaceView.java @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.biometrics; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.PixelFormat; +import android.graphics.RectF; +import android.util.AttributeSet; +import android.util.Log; +import android.view.Surface; +import android.view.SurfaceHolder; +import android.view.SurfaceView; + +import com.android.systemui.R; + +/** + * Surface View for providing the Global High-Brightness Mode (GHBM) illumination for UDFPS. + */ +public class UdfpsSurfaceView extends SurfaceView implements SurfaceHolder.Callback { + private static final String TAG = "UdfpsSurfaceView"; + /** + * Notifies {@link UdfpsView} when to enable GHBM illumination. + */ + interface GhbmIlluminationListener { + /** + * @param surface the surface for which GHBM should be enabled. + * @param onIlluminatedRunnable a runnable that should be run after GHBM is enabled. + */ + void enableGhbm(@NonNull Surface surface, @Nullable Runnable onIlluminatedRunnable); + } + @NonNull private final SurfaceHolder mHolder; + @NonNull private final Paint mSensorPaint; + @Nullable private GhbmIlluminationListener mGhbmIlluminationListener; + @Nullable private Runnable mOnIlluminatedRunnable; + boolean mAwaitingSurfaceToStartIllumination; + boolean mHasValidSurface; + + private Drawable mUdfpsIconPressed; + + public UdfpsSurfaceView(Context context, AttributeSet attrs) { + super(context, attrs); + // Make this SurfaceView draw on top of everything else in this window. This allows us to + // 1) Always show the HBM circle on top of everything else, and + // 2) Properly composite this view with any other animations in the same window no matter + // what contents are added in which order to this view hierarchy. + setZOrderOnTop(true); + mHolder = getHolder(); + mHolder.addCallback(this); + mHolder.setFormat(PixelFormat.RGBA_8888); + mSensorPaint = new Paint(0 /* flags */); + mSensorPaint.setAntiAlias(true); + mSensorPaint.setColor(context.getColor(R.color.config_udfpsColor)); + mSensorPaint.setStyle(Paint.Style.FILL); + + mUdfpsIconPressed = context.getDrawable(R.drawable.udfps_icon_pressed); + } + @Override public void surfaceCreated(SurfaceHolder holder) { + mHasValidSurface = true; + if (mAwaitingSurfaceToStartIllumination) { + doIlluminate(mOnIlluminatedRunnable); + mOnIlluminatedRunnable = null; + mAwaitingSurfaceToStartIllumination = false; + } + } + @Override + public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { + // Unused. + } + @Override public void surfaceDestroyed(SurfaceHolder holder) { + mHasValidSurface = false; + } + void setGhbmIlluminationListener(@Nullable GhbmIlluminationListener listener) { + mGhbmIlluminationListener = listener; + } + /** + * Note: there is no corresponding method to stop GHBM illumination. It is expected that + * {@link UdfpsView} will hide this view, which would destroy the surface and remove the + * illumination dot. + */ + void startGhbmIllumination(@Nullable Runnable onIlluminatedRunnable) { + if (mGhbmIlluminationListener == null) { + Log.e(TAG, "startIllumination | mGhbmIlluminationListener is null"); + return; + } + if (mHasValidSurface) { + doIlluminate(onIlluminatedRunnable); + } else { + mAwaitingSurfaceToStartIllumination = true; + mOnIlluminatedRunnable = onIlluminatedRunnable; + } + } + private void doIlluminate(@Nullable Runnable onIlluminatedRunnable) { + if (mGhbmIlluminationListener == null) { + Log.e(TAG, "doIlluminate | mGhbmIlluminationListener is null"); + return; + } + mGhbmIlluminationListener.enableGhbm(mHolder.getSurface(), onIlluminatedRunnable); + } + /** + * Immediately draws the illumination dot on this SurfaceView's surface. + */ + void drawIlluminationDot(@NonNull RectF sensorRect) { + if (!mHasValidSurface) { + Log.e(TAG, "drawIlluminationDot | the surface is destroyed or was never created."); + return; + } + Canvas canvas = null; + try { + canvas = mHolder.lockCanvas(); + mUdfpsIconPressed.setBounds( + Math.round(sensorRect.left), + Math.round(sensorRect.top), + Math.round(sensorRect.right), + Math.round(sensorRect.bottom) + ); + mUdfpsIconPressed.draw(canvas); + canvas.drawOval(sensorRect, mSensorPaint); + } finally { + // Make sure the surface is never left in a bad state. + if (canvas != null) { + mHolder.unlockCanvasAndPost(canvas); + } + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.kt index a15456d46897..9dcd2db4d3b4 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.kt @@ -24,6 +24,7 @@ import android.graphics.RectF import android.util.AttributeSet import android.util.Log import android.view.MotionEvent +import android.view.Surface import android.widget.FrameLayout import com.android.systemui.R import com.android.systemui.doze.DozeReceiver @@ -56,6 +57,8 @@ class UdfpsView( a.getFloat(R.styleable.UdfpsView_sensorTouchAreaCoefficient, 0f) } + private var ghbmView: UdfpsSurfaceView? = null + /** View controller (can be different for enrollment, BiometricPrompt, Keyguard, etc.). */ var animationViewController: UdfpsAnimationViewController<*>? = null @@ -82,6 +85,10 @@ class UdfpsView( return (animationViewController == null || !animationViewController!!.shouldPauseAuth()) } + override fun onFinishInflate() { + ghbmView = findViewById(R.id.hbm_view) + } + override fun dozeTimeTick() { animationViewController?.dozeTimeTick() } @@ -143,12 +150,34 @@ class UdfpsView( fun configureDisplay(onDisplayConfigured: Runnable) { isDisplayConfigured = true animationViewController?.onDisplayConfiguring() - mUdfpsDisplayMode?.enable(onDisplayConfigured) + val gView = ghbmView + if (gView != null) { + gView.setGhbmIlluminationListener(this::doIlluminate) + gView.visibility = VISIBLE + gView.startGhbmIllumination(onDisplayConfigured) + } else { + doIlluminate(null /* surface */, onDisplayConfigured) + } + } + + private fun doIlluminate(surface: Surface?, onDisplayConfigured: Runnable?) { + if (ghbmView != null && surface == null) { + Log.e(TAG, "doIlluminate | surface must be non-null for GHBM") + } + + mUdfpsDisplayMode?.enable { + onDisplayConfigured?.run() + ghbmView?.drawIlluminationDot(sensorRect) + } } fun unconfigureDisplay() { isDisplayConfigured = false animationViewController?.onDisplayUnconfigured() + ghbmView?.let { view -> + view.setGhbmIlluminationListener(null) + view.visibility = INVISIBLE + } mUdfpsDisplayMode?.disable(null /* onDisabled */) } } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java index 443d2774f0e0..bc130894ced1 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java @@ -93,6 +93,7 @@ import com.android.systemui.util.time.SystemClockImpl; import com.android.systemui.wallet.dagger.WalletModule; import com.android.systemui.wmshell.BubblesManager; +import com.android.systemui.R; import com.android.wm.shell.bubbles.Bubbles; import java.util.Optional; @@ -198,8 +199,17 @@ static SysUiState provideSysUiState(DumpManager dumpManager) { @BindsOptionalOf abstract CentralSurfaces optionalCentralSurfaces(); - @BindsOptionalOf - abstract UdfpsDisplayModeProvider optionalUdfpsDisplayModeProvider(); + @Provides + static UdfpsDisplayModeProvider getUdfpsDisplayModeProvider(Context context) { + String className = context.getString(R.string.config_udfpsDisplayModeProviderComponent); + try { + Class clazz = context.getClassLoader().loadClass(className); + return (UdfpsDisplayModeProvider) clazz.getDeclaredConstructor( + new Class[] { Context.class }).newInstance(context); + } catch (Throwable t) { + throw new RuntimeException("Error loading UdfpsDisplayModeProvider " + className, t); + } + } @BindsOptionalOf abstract AlternateUdfpsTouchProvider optionalUdfpsTouchProvider(); diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java index da6c163b1eea..7c3d4fbab343 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java @@ -48,6 +48,7 @@ import com.android.internal.logging.UiEventLoggerImpl; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.biometrics.AuthController; +import com.android.systemui.R; import com.android.systemui.plugins.SensorManagerPlugin; import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.statusbar.policy.DevicePostureController; @@ -265,14 +266,15 @@ public int getId() { false /* requiresProx */, true /* immediatelyReRegister */), }; - setProxListening(false); // Don't immediately start listening when we register. - mProximitySensor.register( - proximityEvent -> { - if (proximityEvent != null) { - mProxCallback.accept(!proximityEvent.getBelow()); - } - }); - + if (context.getResources().getBoolean(R.bool.doze_proximity_sensor_supported)) { + setProxListening(false); // Don't immediately start listening when we register. + mProximitySensor.register( + proximityEvent -> { + if (proximityEvent != null) { + mProxCallback.accept(!proximityEvent.getBelow()); + } + }); + } mDevicePostureController.addCallback(mDevicePostureCallback); } diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java index 93f13ebb3892..2dc3dda09235 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.java +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.java @@ -197,7 +197,7 @@ public class Flags { /***************************************/ // 900 - media public static final UnreleasedFlag MEDIA_TAP_TO_TRANSFER = new UnreleasedFlag(900); - public static final UnreleasedFlag MEDIA_SESSION_ACTIONS = new UnreleasedFlag(901); + public static final ReleasedFlag MEDIA_SESSION_ACTIONS = new ReleasedFlag(901, true); public static final ReleasedFlag MEDIA_NEARBY_DEVICES = new ReleasedFlag(903); public static final ReleasedFlag MEDIA_MUTE_AWAIT = new ReleasedFlag(904); public static final UnreleasedFlag DREAM_MEDIA_COMPLICATION = new UnreleasedFlag(905); diff --git a/packages/SystemUI/src/com/android/systemui/fragments/ExtensionFragmentListener.java b/packages/SystemUI/src/com/android/systemui/fragments/ExtensionFragmentListener.java index 18fb423b87a5..d9bcb508c8e2 100644 --- a/packages/SystemUI/src/com/android/systemui/fragments/ExtensionFragmentListener.java +++ b/packages/SystemUI/src/com/android/systemui/fragments/ExtensionFragmentListener.java @@ -50,13 +50,12 @@ private ExtensionFragmentListener(View view, String tag, int id, Extension ex @Override public void accept(T extension) { - try { - Fragment.class.cast(extension); + if (Fragment.class.isInstance(extension)) { mFragmentHostManager.getExtensionManager().setCurrentExtension(mId, mTag, mOldClass, extension.getClass().getName(), mExtension.getContext()); mOldClass = extension.getClass().getName(); - } catch (ClassCastException e) { - Log.e(TAG, extension.getClass().getName() + " must be a Fragment", e); + } else { + Log.e(TAG, extension.getClass().getName() + " must be a Fragment"); } mExtension.clearItem(true); } diff --git a/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java b/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java index 9c7411bf3649..15229a1e87c6 100644 --- a/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java +++ b/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java @@ -54,7 +54,8 @@ public class FragmentHostManager { private final View mRootView; private final InterestingConfigChanges mConfigChanges = new InterestingConfigChanges( ActivityInfo.CONFIG_FONT_SCALE | ActivityInfo.CONFIG_LOCALE - | ActivityInfo.CONFIG_ASSETS_PATHS); + | ActivityInfo.CONFIG_SCREEN_LAYOUT | ActivityInfo.CONFIG_ASSETS_PATHS + | ActivityInfo.CONFIG_UI_MODE); private final FragmentService mManager; private final ExtensionFragmentManager mPlugins = new ExtensionFragmentManager(); diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java index 74d5bd577cf4..77b7e478494d 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java @@ -80,8 +80,8 @@ private void onExtensionCallback(GlobalActions newPlugin) { } @Override - public void handleShowShutdownUi(boolean isReboot, String reason) { - mExtension.get().showShutdownUi(isReboot, reason); + public void handleShowShutdownUi(boolean isReboot, String reason, boolean rebootCustom) { + mExtension.get().showShutdownUi(isReboot, reason, rebootCustom); } @Override @@ -116,9 +116,9 @@ public void shutdown() { } @Override - public void reboot(boolean safeMode) { + public void reboot(boolean safeMode, String reason) { try { - mBarService.reboot(safeMode); + mBarService.reboot(safeMode, reason); } catch (RemoteException e) { } } diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java index da5819a7f3bc..94f6d13551f9 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java @@ -24,6 +24,9 @@ import static android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN; import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_2BUTTON; +import android.view.CrossWindowBlurListeners; +import com.android.systemui.statusbar.BlurUtils; +import com.android.systemui.dump.DumpManager; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN; @@ -59,6 +62,7 @@ import android.os.Handler; import android.os.IBinder; import android.os.Message; +import android.os.PowerManager; import android.os.RemoteException; import android.os.SystemProperties; import android.os.UserHandle; @@ -181,6 +185,8 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene private static final String GLOBAL_ACTION_KEY_LOGOUT = "logout"; static final String GLOBAL_ACTION_KEY_EMERGENCY = "emergency"; static final String GLOBAL_ACTION_KEY_SCREENSHOT = "screenshot"; + private static final String GLOBAL_ACTION_KEY_REBOOT_RECOVERY = "reboot_recovery"; + private static final String GLOBAL_ACTION_KEY_REBOOT_BOOTLOADER = "reboot_bootloader"; // See NotificationManagerService#scheduleDurationReachedLocked private static final long TOAST_FADE_TIME = 333; @@ -249,6 +255,13 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; private final DialogLaunchAnimator mDialogLaunchAnimator; + private boolean mUsePowerOptions = false; + private String[] mDefaultMenuActions; + private String[] mRootMenuActions; + private String[] mRebootMenuActions; + private String[] mCurrentMenuActions; + private boolean mRebootMenu; + @VisibleForTesting public enum GlobalActionsEvent implements UiEventLogger.UiEventEnum { @UiEvent(doc = "The global actions / power menu surface became visible on the screen.") @@ -415,6 +428,12 @@ public GlobalActionsDialogLite( mScreenshotHelper = new ScreenshotHelper(context); mConfigurationController.addCallback(this); + + mDefaultMenuActions = mContext.getResources().getStringArray( + com.android.internal.R.array.config_globalActionsList); + mRebootMenuActions = mContext.getResources().getStringArray( + com.android.internal.R.array.config_rebootActionsList); + settingsChanged(); } /** @@ -454,6 +473,8 @@ public void showOrHideDialog(boolean keyguardShowing, boolean isDeviceProvisione @Nullable View view) { mKeyguardShowing = keyguardShowing; mDeviceProvisioned = isDeviceProvisioned; + mCurrentMenuActions = mRootMenuActions; + mRebootMenu = false; if (mDialog != null && mDialog.isShowing()) { // In order to force global actions to hide on the same affordance press, we must // register a call to onGlobalActionsShown() first to prevent the default actions @@ -575,22 +596,74 @@ protected void createActionItems() { mItems.clear(); mOverflowItems.clear(); mPowerItems.clear(); - String[] defaultActions = getDefaultActions(); + + mUsePowerOptions = false; + buildMenuList(); + } + + public void settingsChanged() { + final String globalAction = Settings.System.getStringForUser(mContext.getContentResolver(), + Settings.System.GLOBAL_ACTIONS_LIST, UserHandle.USER_CURRENT); + + if (globalAction != null) { + mRootMenuActions = globalAction.split(","); + } else { + mRootMenuActions = mDefaultMenuActions; + } + } + + private boolean advancedRebootEnabled(Context context) { + boolean advancedRebootEnabled = Settings.Secure.getIntForUser(context.getContentResolver(), + Settings.Secure.ADVANCED_REBOOT, 0, UserHandle.USER_CURRENT) == 1; + return advancedRebootEnabled; + } + + private List getCurrentRebootMenuItems() { + List items = new ArrayList(); + + String[] rebootMenuActions = mContext.getResources().getStringArray( + com.android.internal.R.array.config_rebootActionsList); + for (String actionKey : rebootMenuActions) { + if (advancedRebootEnabled(mContext) && GLOBAL_ACTION_KEY_REBOOT_RECOVERY.equals(actionKey)) { + RebootRecoveryAction a = new RebootRecoveryAction(); + addIfShouldShowAction(items, a); + } else if (advancedRebootEnabled(mContext) && GLOBAL_ACTION_KEY_REBOOT_BOOTLOADER.equals(actionKey)) { + RebootBootloaderAction a = new RebootBootloaderAction(); + addIfShouldShowAction(items, a); + } + } + return items; + } + + private boolean showRebootSubmenu() { + List rebootMenuItems = getCurrentRebootMenuItems(); + return rebootMenuItems.size() > 0 && !mUsePowerOptions; + } + + private boolean usePowerOptionsMenu(List actions, Action shutdownAction, Action restartAction) { + return actions.contains(shutdownAction) && actions.contains(restartAction) + && actions.size() > getMaxShownPowerItems(); + } + + private void buildMenuList() { + List tempActions = new ArrayList<>(); + + ArraySet addedKeys = new ArraySet(); ShutDownAction shutdownAction = new ShutDownAction(); RestartAction restartAction = new RestartAction(); - ArraySet addedKeys = new ArraySet<>(); - List tempActions = new ArrayList<>(); - CurrentUserProvider currentUser = new CurrentUserProvider(); + RebootRecoveryAction rbtRecoveryAction = new RebootRecoveryAction(); + RebootBootloaderAction rbtBlAction = new RebootBootloaderAction(); // make sure emergency affordance action is first, if needed - if (mEmergencyAffordanceManager.needsEmergencyAffordance()) { + if (mEmergencyAffordanceManager.needsEmergencyAffordance() && !mRebootMenu) { addIfShouldShowAction(tempActions, new EmergencyAffordanceAction()); addedKeys.add(GLOBAL_ACTION_KEY_EMERGENCY); } - for (int i = 0; i < defaultActions.length; i++) { - String actionKey = defaultActions[i]; + CurrentUserProvider currentUser = new CurrentUserProvider(); + + for (String actionKey : mCurrentMenuActions) { if (addedKeys.contains(actionKey)) { // If we already have added this, don't add it again. continue; @@ -623,6 +696,10 @@ protected void createActionItems() { addIfShouldShowAction(tempActions, getAssistAction()); } else if (GLOBAL_ACTION_KEY_RESTART.equals(actionKey)) { addIfShouldShowAction(tempActions, restartAction); + } else if (advancedRebootEnabled(mContext) && GLOBAL_ACTION_KEY_REBOOT_RECOVERY.equals(actionKey)) { + addIfShouldShowAction(tempActions, rbtRecoveryAction); + } else if (advancedRebootEnabled(mContext) && GLOBAL_ACTION_KEY_REBOOT_BOOTLOADER.equals(actionKey)) { + addIfShouldShowAction(tempActions, rbtBlAction); } else if (GLOBAL_ACTION_KEY_SCREENSHOT.equals(actionKey)) { addIfShouldShowAction(tempActions, new ScreenshotAction()); } else if (GLOBAL_ACTION_KEY_LOGOUT.equals(actionKey)) { @@ -634,7 +711,7 @@ protected void createActionItems() { && currentUser.get().id != UserHandle.USER_SYSTEM) { addIfShouldShowAction(tempActions, new LogoutAction()); } - } else if (GLOBAL_ACTION_KEY_EMERGENCY.equals(actionKey)) { + } else if (GLOBAL_ACTION_KEY_EMERGENCY.equals(actionKey) && !mRebootMenu) { if (shouldDisplayEmergency()) { addIfShouldShowAction(tempActions, new EmergencyDialerAction()); } @@ -646,19 +723,32 @@ protected void createActionItems() { } // replace power and restart with a single power options action, if needed - if (tempActions.contains(shutdownAction) && tempActions.contains(restartAction) - && tempActions.size() > getMaxShownPowerItems()) { + if (usePowerOptionsMenu(tempActions, shutdownAction, restartAction)) { + mUsePowerOptions = true; // transfer shutdown and restart to their own list of power actions int powerOptionsIndex = Math.min(tempActions.indexOf(restartAction), tempActions.indexOf(shutdownAction)); tempActions.remove(shutdownAction); tempActions.remove(restartAction); + if (advancedRebootEnabled(mContext)) { + tempActions.remove(rbtRecoveryAction); + tempActions.remove(rbtBlAction); + } mPowerItems.add(shutdownAction); mPowerItems.add(restartAction); + if (advancedRebootEnabled(mContext)) { + mPowerItems.add(rbtRecoveryAction); + mPowerItems.add(rbtBlAction); + } // add the PowerOptionsAction after Emergency, if present tempActions.add(powerOptionsIndex, new PowerOptionsAction()); } + if (tempActions.contains(restartAction)) { + tempActions.set(tempActions.indexOf(restartAction), new RestartAction()); + } else if (mPowerItems.contains(restartAction)) { + mPowerItems.set(mPowerItems.indexOf(restartAction), new RestartAction()); + } for (Action action : tempActions) { addActionItem(action); } @@ -782,7 +872,7 @@ final class ShutDownAction extends SinglePressAction implements LongPressAction public boolean onLongPress() { mUiEventLogger.log(GlobalActionsEvent.GA_SHUTDOWN_LONG_PRESS); if (!mUserManager.hasUserRestriction(UserManager.DISALLOW_SAFE_BOOT)) { - mWindowManagerFuncs.reboot(true); + mWindowManagerFuncs.reboot(true, null); return true; } return false; @@ -906,14 +996,20 @@ EmergencyDialerAction makeEmergencyDialerActionForTesting() { @VisibleForTesting final class RestartAction extends SinglePressAction implements LongPressAction { RestartAction() { - super(R.drawable.ic_restart, R.string.global_action_restart); + super(R.drawable.ic_restart, com.android.systemui.R.string.global_action_reboot); + if (mRebootMenu) { + mMessageResId = com.android.systemui.R.string.global_action_reboot_sub; + mIconResId = com.android.systemui.R.drawable.ic_restart_system; + } else if (showRebootSubmenu() && advancedRebootEnabled(mContext)) { + mMessageResId = com.android.systemui.R.string.global_action_reboot_more; + } } @Override public boolean onLongPress() { mUiEventLogger.log(GlobalActionsEvent.GA_REBOOT_LONG_PRESS); if (!mUserManager.hasUserRestriction(UserManager.DISALLOW_SAFE_BOOT)) { - mWindowManagerFuncs.reboot(true); + mWindowManagerFuncs.reboot(true, null); return true; } return false; @@ -932,7 +1028,70 @@ public boolean showBeforeProvisioning() { @Override public void onPress() { mUiEventLogger.log(GlobalActionsEvent.GA_REBOOT_PRESS); - mWindowManagerFuncs.reboot(false); + if (!mRebootMenu && showRebootSubmenu()) { + mRebootMenu = true; + mCurrentMenuActions = mRebootMenuActions; + handleShow(null); + } else { + mHandler.sendEmptyMessage(MESSAGE_DISMISS); + doReboot(); + } + } + + private void doReboot() { + mWindowManagerFuncs.reboot(false, null); + } + } + + private final class RebootRecoveryAction extends SinglePressAction { + private RebootRecoveryAction() { + super(com.android.systemui.R.drawable.ic_restart_recovery, com.android.systemui.R.string.global_action_reboot_recovery); + } + + @Override + public boolean showDuringKeyguard() { + return true; + } + + @Override + public boolean showDuringRestrictedKeyguard() { + return false; + } + + @Override + public boolean showBeforeProvisioning() { + return true; + } + + @Override + public void onPress() { + mWindowManagerFuncs.reboot(false, PowerManager.REBOOT_RECOVERY); + } + } + + private final class RebootBootloaderAction extends SinglePressAction { + private RebootBootloaderAction() { + super(com.android.systemui.R.drawable.ic_restart_bootloader, com.android.systemui.R.string.global_action_reboot_bootloader); + } + + @Override + public boolean showDuringKeyguard() { + return true; + } + + @Override + public boolean showDuringRestrictedKeyguard() { + return false; + } + + @Override + public boolean showBeforeProvisioning() { + return true; + } + + @Override + public void onPress() { + mWindowManagerFuncs.reboot(false, PowerManager.REBOOT_BOOTLOADER); } } @@ -1085,7 +1244,7 @@ public void onPress() { } private Action getSettingsAction() { - return new SinglePressAction(R.drawable.ic_settings, + return new SinglePressAction(com.android.systemui.R.drawable.ic_lock_settings, R.string.global_action_settings) { @Override @@ -1606,6 +1765,14 @@ public interface Action { */ boolean showDuringKeyguard(); + /** + * @return whether this action should appear in the dialog when a restricted + * keyguard is showing. + */ + default boolean showDuringRestrictedKeyguard() { + return true; + } + /** * @return whether this action should appear in the dialog before the * device is provisioned.f @@ -1661,9 +1828,9 @@ private interface LongPressAction extends Action { */ private abstract class SinglePressAction implements Action { - private final int mIconResId; + protected int mIconResId; private final Drawable mIcon; - private final int mMessageResId; + protected int mMessageResId; private final CharSequence mMessage; protected SinglePressAction(int iconResId, int messageResId) { @@ -1813,6 +1980,11 @@ private boolean isOn() { public CharSequence getMessage() { return null; } + + public String getStatus() { + return null; + } + @Override public int getMessageResId() { return isOn() ? mEnabledStatusMessageResId : mDisabledStatusMessageResId; @@ -1924,10 +2096,12 @@ protected void changeStateFromPress(boolean buttonOn) { } } + @Override public boolean showDuringKeyguard() { return true; } + @Override public boolean showBeforeProvisioning() { return false; } @@ -1950,10 +2124,12 @@ void onToggle(boolean on) { } } + @Override public boolean showDuringKeyguard() { return true; } + @Override public boolean showBeforeProvisioning() { return false; } @@ -2167,7 +2343,6 @@ static class ActionsDialogLite extends SystemUIDialog implements DialogInterface protected Drawable mBackgroundDrawable; protected final SysuiColorExtractor mColorExtractor; private boolean mKeyguardShowing; - protected float mScrimAlpha; protected final NotificationShadeWindowController mNotificationShadeWindowController; private ListPopupWindow mOverflowPopup; private Dialog mPowerOptionsDialog; @@ -2178,6 +2353,7 @@ static class ActionsDialogLite extends SystemUIDialog implements DialogInterface private KeyguardUpdateMonitor mKeyguardUpdateMonitor; private LockPatternUtils mLockPatternUtils; private float mWindowDimAmount; + private BlurUtils mBlurUtils; protected ViewGroup mContainer; @@ -2266,6 +2442,8 @@ void setBackDispatcherOverride(OnBackInvokedDispatcher mockDispatcher) { mKeyguardUpdateMonitor = keyguardUpdateMonitor; mLockPatternUtils = lockPatternUtils; mGestureDetector = new GestureDetector(mContext, mGestureListener); + mBlurUtils = new BlurUtils(mContext.getResources(), + CrossWindowBlurListeners.getInstance(), new DumpManager()); } @Override @@ -2394,9 +2572,10 @@ public boolean dispatchPopulateAccessibilityEvent( if (mBackgroundDrawable == null) { mBackgroundDrawable = new ScrimDrawable(); - mScrimAlpha = 1.0f; } + getWindow().setDimAmount(mBlurUtils.supportsBlursOnWindows() ? 0.54f : 0.88f); + // If user entered from the lock screen and smart lock was enabled, disable it int user = KeyguardUpdateMonitor.getCurrentUser(); boolean userHasTrust = mKeyguardUpdateMonitor.getUserHasTrust(user); diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java index 290bf0d0734c..36d7f95abe95 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java @@ -79,7 +79,7 @@ public void showGlobalActions(GlobalActionsManager manager) { } @Override - public void showShutdownUi(boolean isReboot, String reason) { + public void showShutdownUi(boolean isReboot, String reason, boolean rebootCustom) { ScrimDrawable background = new ScrimDrawable(); final Dialog d = new Dialog(mContext, @@ -144,7 +144,7 @@ public void showShutdownUi(boolean isReboot, String reason) { messageView.setTextColor(color); messageView.setText(getRebootMessage(isReboot, reason)); - String rebootReasonMessage = getReasonMessage(reason); + String rebootReasonMessage = getReasonMessage(reason, rebootCustom); if (rebootReasonMessage != null) { reasonView.setVisibility(View.VISIBLE); reasonView.setText(rebootReasonMessage); @@ -158,7 +158,9 @@ private int getRebootMessage(boolean isReboot, @Nullable String reason) { if (reason != null && reason.startsWith(PowerManager.REBOOT_RECOVERY_UPDATE)) { return R.string.reboot_to_update_reboot; } else if (reason != null && reason.equals(PowerManager.REBOOT_RECOVERY)) { - return R.string.reboot_to_reset_message; + return com.android.internal.R.string.reboot_to_recovery_message; + } else if (reason != null && reason.equals(PowerManager.REBOOT_BOOTLOADER)) { + return com.android.internal.R.string.reboot_to_bootloader_message; } else if (isReboot) { return R.string.reboot_to_reset_message; } else { @@ -167,10 +169,10 @@ private int getRebootMessage(boolean isReboot, @Nullable String reason) { } @Nullable - private String getReasonMessage(@Nullable String reason) { + private String getReasonMessage(@Nullable String reason, boolean custom) { if (reason != null && reason.startsWith(PowerManager.REBOOT_RECOVERY_UPDATE)) { return mContext.getString(R.string.reboot_to_update_title); - } else if (reason != null && reason.equals(PowerManager.REBOOT_RECOVERY)) { + } else if (reason != null && reason.equals(PowerManager.REBOOT_RECOVERY) && !custom) { return mContext.getString(R.string.reboot_to_reset_title); } else { return null; diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsPowerDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsPowerDialog.java index caa88a372036..ec3e3431d72e 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsPowerDialog.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsPowerDialog.java @@ -19,6 +19,7 @@ import android.app.Dialog; import android.content.Context; import android.content.res.Resources; +import android.view.CrossWindowBlurListeners; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -26,6 +27,11 @@ import android.view.WindowManager; import android.widget.ListAdapter; +import com.android.systemui.statusbar.BlurUtils; +import com.android.systemui.dump.DumpManager; + +import androidx.constraintlayout.helper.widget.Flow; + /** * Creates a customized Dialog for displaying the Shut Down and Restart actions. */ @@ -45,16 +51,21 @@ public static Dialog create(@NonNull Context context, ListAdapter adapter) { Resources res = context.getResources(); - Dialog dialog = new Dialog(context); + Dialog dialog = new Dialog(context, + com.android.systemui.R.style.Theme_SystemUI_Dialog_GlobalActionsLite); dialog.requestWindowFeature(Window.FEATURE_NO_TITLE); dialog.setContentView(listView); + BlurUtils blurUtils = new BlurUtils(context.getResources(), + CrossWindowBlurListeners.getInstance(), new DumpManager()); + Window window = dialog.getWindow(); window.setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY); window.setTitle(""); // prevent Talkback from speaking first item name twice window.setBackgroundDrawable(res.getDrawable( com.android.systemui.R.drawable.control_background, context.getTheme())); window.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM); + window.setDimAmount(blurUtils.supportsBlursOnWindows() ? 0.54f : 0.88f); return dialog; } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java index f84a5e39163f..cc77bcc281f9 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java @@ -144,10 +144,10 @@ public void updateIndication(@IndicationType int type, KeyguardIndication newInd boolean currMsgShownForMinTime = timeSinceLastIndicationSwitch >= minShowDuration; if (hasNewIndication) { if (mCurrIndicationType == INDICATION_TYPE_NONE || mCurrIndicationType == type) { - showIndication(type); + showIndication(type, showAsap); } else if (showAsap) { if (currMsgShownForMinTime) { - showIndication(type); + showIndication(type, showAsap); } else { mIndicationQueue.removeIf(x -> x == type); mIndicationQueue.add(0 /* index */, type /* type */); @@ -158,7 +158,7 @@ public void updateIndication(@IndicationType int type, KeyguardIndication newInd getMinVisibilityMillis(mIndicationMessages.get(type)), DEFAULT_INDICATION_SHOW_LENGTH); if (timeSinceLastIndicationSwitch >= nextShowTime) { - showIndication(type); + showIndication(type, showAsap); } else { scheduleShowNextIndication( nextShowTime - timeSinceLastIndicationSwitch); @@ -175,7 +175,7 @@ public void updateIndication(@IndicationType int type, KeyguardIndication newInd if (mShowNextIndicationRunnable != null) { mShowNextIndicationRunnable.runImmediately(); } else { - showIndication(INDICATION_TYPE_NONE); + showIndication(INDICATION_TYPE_NONE, true); } } else { scheduleShowNextIndication(minShowDuration - timeSinceLastIndicationSwitch); @@ -241,7 +241,7 @@ public void clearMessages() { * Will re-add this indication to be re-shown after all other indications have been * rotated through. */ - private void showIndication(@IndicationType int type) { + private void showIndication(@IndicationType int type, boolean showAsap) { cancelScheduledIndication(); final CharSequence previousMessage = mCurrMessage; @@ -259,7 +259,7 @@ private void showIndication(@IndicationType int type) { mLastIndicationSwitch = SystemClock.uptimeMillis(); if (!TextUtils.equals(previousMessage, mCurrMessage) || previousIndicationType != mCurrIndicationType) { - mView.switchIndication(mIndicationMessages.get(type)); + mView.switchIndication(mIndicationMessages.get(type), showAsap); } // only schedule next indication if there's more than just this indication in the queue @@ -311,9 +311,9 @@ public void onDozingChanged(boolean isDozing) { if (isDozing == mIsDozing) return; mIsDozing = isDozing; if (mIsDozing) { - showIndication(INDICATION_TYPE_NONE); + showIndication(INDICATION_TYPE_NONE, true); } else if (mIndicationQueue.size() > 0) { - showIndication(mIndicationQueue.get(0)); + showIndication(mIndicationQueue.get(0), true); } } }; @@ -331,7 +331,7 @@ class ShowNextIndication { mShowIndicationRunnable = () -> { int type = mIndicationQueue.size() == 0 ? INDICATION_TYPE_NONE : mIndicationQueue.get(0); - showIndication(type); + showIndication(type, true); }; mCancelDelayedRunnable = mExecutor.executeDelayed(mShowIndicationRunnable, delay); } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java index 2a737970907a..4ee57ac63bb2 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java @@ -25,7 +25,9 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.database.ContentObserver; import android.graphics.Typeface; +import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; import android.icu.text.DateFormat; import android.icu.text.DisplayContext; @@ -34,10 +36,12 @@ import android.net.Uri; import android.os.Handler; import android.os.Trace; +import android.os.UserHandle; import android.provider.Settings; import android.service.notification.ZenModeConfig; import android.text.TextUtils; import android.text.style.StyleSpan; +import android.util.Log; import androidx.core.graphics.drawable.IconCompat; import androidx.slice.Slice; @@ -68,6 +72,8 @@ import javax.inject.Inject; +import com.android.internal.util.omni.OmniJawsClient; + /** * Simple Slice provider that shows the current date. * @@ -77,7 +83,9 @@ public class KeyguardSliceProvider extends SliceProvider implements NextAlarmController.NextAlarmChangeCallback, ZenModeController.Callback, NotificationMediaManager.MediaListener, StatusBarStateController.StateListener, - SystemUIAppComponentFactory.ContextInitializer { + SystemUIAppComponentFactory.ContextInitializer, OmniJawsClient.OmniJawsObserver { + + private static final boolean DEBUG = false; private static final String TAG = "KgdSliceProvider"; @@ -86,6 +94,7 @@ public class KeyguardSliceProvider extends SliceProvider implements private static final String KEYGUARD_HEADER_URI = "content://com.android.systemui.keyguard/header"; public static final String KEYGUARD_DATE_URI = "content://com.android.systemui.keyguard/date"; + public static final String KEYGUARD_WEATHER_URI = "content://com.android.systemui.keyguard/weather"; public static final String KEYGUARD_NEXT_ALARM_URI = "content://com.android.systemui.keyguard/alarm"; public static final String KEYGUARD_DND_URI = "content://com.android.systemui.keyguard/dnd"; @@ -106,6 +115,7 @@ public class KeyguardSliceProvider extends SliceProvider implements protected final Uri mSliceUri; protected final Uri mHeaderUri; protected final Uri mDateUri; + protected final Uri mWeatherUri; protected final Uri mAlarmUri; protected final Uri mDndUri; protected final Uri mMediaUri; @@ -147,6 +157,12 @@ public class KeyguardSliceProvider extends SliceProvider implements private boolean mMediaIsVisible; private SystemUIAppComponentFactory.ContextAvailableCallback mContextAvailableCallback; + private OmniJawsClient mWeatherClient; + private OmniJawsClient.WeatherInfo mWeatherInfo; + private OmniJawsClient.PackageInfo mPackageInfo; + private boolean mWeatherEnabled; + private boolean mShowWeatherSlice; + /** * Receiver responsible for time ticking and updating the date format. */ @@ -195,6 +211,7 @@ public KeyguardSliceProvider() { mSliceUri = Uri.parse(KEYGUARD_SLICE_URI); mHeaderUri = Uri.parse(KEYGUARD_HEADER_URI); mDateUri = Uri.parse(KEYGUARD_DATE_URI); + mWeatherUri = Uri.parse(KEYGUARD_WEATHER_URI); mAlarmUri = Uri.parse(KEYGUARD_NEXT_ALARM_URI); mDndUri = Uri.parse(KEYGUARD_DND_URI); mMediaUri = Uri.parse(KEYGUARD_MEDIA_URI); @@ -212,6 +229,7 @@ public Slice onBindSlice(Uri sliceUri) { } else { builder.addRow(new RowBuilder(mDateUri).setTitle(mLastText)); } + addWeather(builder); addNextAlarmLocked(builder); addZenModeLocked(builder); addPrimaryActionLocked(builder); @@ -300,6 +318,96 @@ protected boolean isDndOn() { return mZenModeController.getZen() != Settings.Global.ZEN_MODE_OFF; } + protected void addWeather(ListBuilder builder) { + if (!mWeatherClient.isOmniJawsEnabled()) return; + if (!mWeatherEnabled || !mShowWeatherSlice || mWeatherInfo == null || mPackageInfo == null) { + return; + } + String temperatureText = mWeatherInfo.temp + " " + mWeatherInfo.tempUnits; + Icon conditionIcon = Icon.createWithResource(mPackageInfo.packageName, mPackageInfo.resourceID); + RowBuilder weatherRowBuilder = new RowBuilder(mWeatherUri) + .setTitle(temperatureText) + .addEndItem(IconCompat.createFromIcon(conditionIcon), ListBuilder.ICON_IMAGE); + builder.addRow(weatherRowBuilder); + } + + @Override + public void weatherUpdated() { + queryAndUpdateWeather(); + mContentResolver.notifyChange(mSliceUri, null /* observer */); + } + + @Override + public void weatherError(int errorReason) { + if (DEBUG) Log.d(TAG, "weatherError " + errorReason); + } + + private void queryAndUpdateWeather() { + try { + if (DEBUG) Log.d(TAG, "queryAndUpdateWeather.isOmniJawsEnabled " + mWeatherClient.isOmniJawsEnabled()); + mWeatherClient.queryWeather(); + mWeatherInfo = mWeatherClient.getWeatherInfo(); + setPackageInfo(); + if (DEBUG) Log.w(TAG, "queryAndUpdateWeather mPackageName: " + mPackageInfo.packageName); + if (DEBUG) Log.w(TAG, "queryAndUpdateWeather mDrawableResID: " + mPackageInfo.resourceID); + } catch(Exception e) { + // Do nothing + } + } + + private void setPackageInfo() { + mPackageInfo = null; + if (mWeatherInfo != null){ + Drawable conditionImage = mWeatherClient.getWeatherConditionImage(mWeatherInfo.conditionCode); + mPackageInfo = mWeatherClient.getPackageInfo(); + } + } + + private WeatherSettingsObserver mWeatherSettingsObserver; + + private class WeatherSettingsObserver extends ContentObserver { + WeatherSettingsObserver(Handler handler) { + super(handler); + } + + void observe() { + mContentResolver.registerContentObserver(Settings.System.getUriFor( + Settings.System.OMNI_LOCKSCREEN_WEATHER_ENABLED), + false, this, UserHandle.USER_ALL); + + mContentResolver.registerContentObserver(Settings.System.getUriFor( + Settings.System.OMNIJAWS_WEATHER_ICON_PACK), + false, this, UserHandle.USER_ALL); + + mContentResolver.registerContentObserver(Settings.System.getUriFor( + Settings.System.AICP_LOCKSCREEN_WEATHER_STYLE), + false, this, UserHandle.USER_ALL); + } + + @Override + public void onChange(boolean selfChange, Uri uri) { + super.onChange(selfChange, uri); + if (uri.equals(Settings.System.getUriFor(Settings.System.OMNI_LOCKSCREEN_WEATHER_ENABLED))) { + updateLockscreenWeather(); + mContentResolver.notifyChange(mSliceUri, null /* observer */); + } else if (uri.equals(Settings.System.getUriFor(Settings.System.OMNIJAWS_WEATHER_ICON_PACK))) { + queryAndUpdateWeather(); + mContentResolver.notifyChange(mSliceUri, null /* observer */); + } else if (uri.equals(Settings.System.getUriFor(Settings.System.AICP_LOCKSCREEN_WEATHER_STYLE))) { + updateLockscreenWeatherStyle(); + mContentResolver.notifyChange(mSliceUri, null /* observer */); + } + } + + public void updateLockscreenWeather() { + mWeatherEnabled = Settings.System.getIntForUser(mContentResolver, Settings.System.OMNI_LOCKSCREEN_WEATHER_ENABLED, 0, UserHandle.USER_CURRENT) != 0; + } + + public void updateLockscreenWeatherStyle() { + mShowWeatherSlice = Settings.System.getIntForUser(mContentResolver, Settings.System.AICP_LOCKSCREEN_WEATHER_STYLE, 0, UserHandle.USER_CURRENT) != 0; + } + } + @Override public boolean onCreateSliceProvider() { mContextAvailableCallback.onContextAvailable(getContext()); @@ -318,6 +426,14 @@ public boolean onCreateSliceProvider() { mStatusBarStateController.addCallback(this); mNextAlarmController.addCallback(this); mZenModeController.addCallback(this); + mWeatherSettingsObserver = new WeatherSettingsObserver(mHandler); + mWeatherSettingsObserver.updateLockscreenWeatherStyle(); + mWeatherSettingsObserver.updateLockscreenWeather(); + mWeatherSettingsObserver.observe(); + mWeatherClient = new OmniJawsClient(getContext()); + mWeatherClient.addSettingsObserver(); + mWeatherClient.addObserver(this); + queryAndUpdateWeather(); KeyguardSliceProvider.sInstance = this; registerClockUpdate(); updateClockLocked(); @@ -332,6 +448,8 @@ protected void onDestroy() { mZenModeController.removeCallback(this); mMediaWakeLock.setAcquired(false); mAlarmManager.cancel(mUpdateNextAlarm); + mWeatherClient.cleanupObserver(); + mWeatherClient.removeObserver(this); if (mRegistered) { mRegistered = false; mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateMonitorCallback); diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index 38b98eb45aec..7f8688510431 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -25,7 +25,6 @@ import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_OCCLUSION; import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_TRANSITION_FROM_AOD; import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_UNLOCK_ANIMATION; -import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT; @@ -74,6 +73,7 @@ import android.provider.Settings; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; +import android.util.DisplayUtils; import android.util.EventLog; import android.util.Log; import android.util.Slog; @@ -804,9 +804,6 @@ public int getBouncerPromptReason() { } else if (trustAgentsEnabled && (strongAuth & SOME_AUTH_REQUIRED_AFTER_USER_REQUEST) != 0) { return KeyguardSecurityView.PROMPT_REASON_USER_REQUEST; - } else if (trustAgentsEnabled - && (strongAuth & SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED) != 0) { - return KeyguardSecurityView.PROMPT_REASON_TRUSTAGENT_EXPIRED; } else if (any && ((strongAuth & STRONG_AUTH_REQUIRED_AFTER_LOCKOUT) != 0 || mUpdateMonitor.isFingerprintLockedOut())) { return KeyguardSecurityView.PROMPT_REASON_AFTER_LOCKOUT; @@ -1173,8 +1170,11 @@ public KeyguardViewMediator( mActivityLaunchAnimator = activityLaunchAnimator; - mPowerButtonY = context.getResources().getDimensionPixelSize( - R.dimen.physical_power_button_center_screen_location_y); + final float scaleFactor = DisplayUtils.getScaleFactor(mContext); + int positionY = (int) (scaleFactor * mContext.getResources().getDimensionPixelSize( + R.dimen.physical_power_button_center_screen_location_y)); + + mPowerButtonY = positionY; mWindowCornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(context); } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java b/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java index 017b65acd1d2..7ee051d9198a 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java @@ -26,6 +26,7 @@ import android.os.RemoteException; import android.os.Trace; import android.util.DisplayMetrics; +import android.util.DisplayUtils; import androidx.annotation.Nullable; @@ -209,19 +210,17 @@ private void updateLastSleepOriginLocation() { * Returns the point on the screen closest to the physical power button. */ private Point getPowerButtonOrigin() { + final float scaleFactor = DisplayUtils.getScaleFactor(mContext); + int positionY = (int) (scaleFactor * mContext.getResources().getDimensionPixelSize( + R.dimen.physical_power_button_center_screen_location_y)); + final boolean isPortrait = mContext.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT; if (isPortrait) { - return new Point( - mDisplayMetrics.widthPixels, - mContext.getResources().getDimensionPixelSize( - R.dimen.physical_power_button_center_screen_location_y)); + return new Point(mDisplayMetrics.widthPixels, positionY); } else { - return new Point( - mContext.getResources().getDimensionPixelSize( - R.dimen.physical_power_button_center_screen_location_y), - mDisplayMetrics.heightPixels); + return new Point(positionY, mDisplayMetrics.heightPixels); } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt index 95acc0b8564e..86ca7f515040 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt @@ -85,6 +85,10 @@ constructor( } } + fun updateSettings() { + registry.updateSettings() + } + private fun quickAffordanceInternal( position: KeyguardQuickAffordancePosition ): Flow { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/CameraKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/CameraKeyguardQuickAffordanceConfig.kt new file mode 100644 index 000000000000..59bc8ea13e7e --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/CameraKeyguardQuickAffordanceConfig.kt @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * (C) 2022 Paranoid Android + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.android.systemui.keyguard.domain.quickaffordance + +import com.android.keyguard.KeyguardUpdateMonitor +import com.android.keyguard.KeyguardUpdateMonitorCallback +import com.android.systemui.R +import com.android.systemui.animation.ActivityLaunchAnimator +import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging +import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow +import com.android.systemui.camera.CameraGestureHelper +import com.android.systemui.containeddrawable.ContainedDrawable +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.statusbar.StatusBarState +import javax.inject.Inject +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.Flow + +/** Camera quick affordance data source. */ +@SysUISingleton +class CameraKeyguardQuickAffordanceConfig +@Inject +constructor( + private val cameraGestureHelper: CameraGestureHelper, + private val keyguardUpdateMonitor: KeyguardUpdateMonitor, +) : KeyguardQuickAffordanceConfig { + + override val state: Flow = conflatedCallbackFlow { + val callback = + object : KeyguardUpdateMonitorCallback() { + override fun onKeyguardVisibilityChanged(showing: Boolean) { + trySendWithFailureLogging(state(), TAG) + } + } + + keyguardUpdateMonitor.registerCallback(callback) + + awaitClose { + keyguardUpdateMonitor.removeCallback(callback) + } + } + + override fun onQuickAffordanceClicked( + animationController: ActivityLaunchAnimator.Controller?, + ): KeyguardQuickAffordanceConfig.OnClickedResult { + cameraGestureHelper.launchCamera(-1) + return KeyguardQuickAffordanceConfig.OnClickedResult.Handled + } + + private fun state(): KeyguardQuickAffordanceConfig.State { + return if (cameraGestureHelper.canCameraGestureBeLaunched(StatusBarState.KEYGUARD)) { + KeyguardQuickAffordanceConfig.State.Visible( + icon = ContainedDrawable.WithResource(R.drawable.ic_camera_alt_24dp), + contentDescriptionResourceId = R.string.accessibility_camera_button, + ) + } else { + KeyguardQuickAffordanceConfig.State.Hidden + } + } + + companion object { + private const val TAG = "CameraKeyguardQuickAffordanceConfig" + } +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/FlashlightKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/FlashlightKeyguardQuickAffordanceConfig.kt new file mode 100644 index 000000000000..99f9a9eb673f --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/FlashlightKeyguardQuickAffordanceConfig.kt @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * (C) 2022 Paranoid Android + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.android.systemui.keyguard.domain.quickaffordance + +import com.android.systemui.R +import com.android.systemui.animation.ActivityLaunchAnimator +import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging +import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow +import com.android.systemui.containeddrawable.ContainedDrawable +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.statusbar.policy.FlashlightController +import javax.inject.Inject +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.Flow + +/** Flashlight quick affordance data source. */ +@SysUISingleton +class FlashlightKeyguardQuickAffordanceConfig +@Inject +constructor( + private val controller: FlashlightController, +) : KeyguardQuickAffordanceConfig { + + override val state: Flow = conflatedCallbackFlow { + val callback = + object : FlashlightController.FlashlightListener { + override fun onFlashlightChanged(enabled: Boolean) { + trySendWithFailureLogging(state(), TAG) + } + override fun onFlashlightError() { + trySendWithFailureLogging(state(), TAG) + } + override fun onFlashlightAvailabilityChanged(available: Boolean) { + trySendWithFailureLogging(state(), TAG) + } + } + + controller.addCallback(callback) + + awaitClose { + controller.removeCallback(callback) + } + } + + override fun onQuickAffordanceClicked( + animationController: ActivityLaunchAnimator.Controller?, + ): KeyguardQuickAffordanceConfig.OnClickedResult { + controller.setFlashlight(!controller.isEnabled()) + return KeyguardQuickAffordanceConfig.OnClickedResult.Handled + } + + private fun state(): KeyguardQuickAffordanceConfig.State { + return if (controller.hasFlashlight() && controller.isAvailable()) { + KeyguardQuickAffordanceConfig.State.Visible( + icon = ContainedDrawable.WithResource(if (controller.isEnabled()) { + R.drawable.ic_flashlight_off + } else { + R.drawable.ic_flashlight_on + }), + contentDescriptionResourceId = R.string.quick_settings_flashlight_label, + ) + } else { + KeyguardQuickAffordanceConfig.State.Hidden + } + } + + companion object { + private const val TAG = "FlashlightKeyguardQuickAffordanceConfig" + } +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/KeyguardQuickAffordanceRegistry.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/KeyguardQuickAffordanceRegistry.kt index ad40ee7a0183..90d95ea6355f 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/KeyguardQuickAffordanceRegistry.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/KeyguardQuickAffordanceRegistry.kt @@ -17,6 +17,9 @@ package com.android.systemui.keyguard.domain.quickaffordance +import android.content.Context +import android.provider.Settings + import com.android.systemui.keyguard.domain.model.KeyguardQuickAffordancePosition import javax.inject.Inject import kotlin.reflect.KClass @@ -25,29 +28,37 @@ import kotlin.reflect.KClass interface KeyguardQuickAffordanceRegistry { fun getAll(position: KeyguardQuickAffordancePosition): List fun get(configClass: KClass): T + fun updateSettings() } class KeyguardQuickAffordanceRegistryImpl @Inject constructor( - homeControls: HomeControlsKeyguardQuickAffordanceConfig, - quickAccessWallet: QuickAccessWalletKeyguardQuickAffordanceConfig, - qrCodeScanner: QrCodeScannerKeyguardQuickAffordanceConfig, + private val context: Context, + private val homeControls: HomeControlsKeyguardQuickAffordanceConfig, + private val quickAccessWallet: QuickAccessWalletKeyguardQuickAffordanceConfig, + private val qrCodeScanner: QrCodeScannerKeyguardQuickAffordanceConfig, + private val camera: CameraKeyguardQuickAffordanceConfig, + private val flashlight: FlashlightKeyguardQuickAffordanceConfig, ) : KeyguardQuickAffordanceRegistry { - private val configsByPosition = + + private val configsBySetting: Map = mapOf( - KeyguardQuickAffordancePosition.BOTTOM_START to - listOf( - homeControls, - ), - KeyguardQuickAffordancePosition.BOTTOM_END to - listOf( - quickAccessWallet, - qrCodeScanner, - ), + "home" to homeControls, + "wallet" to quickAccessWallet, + "qr" to qrCodeScanner, + "camera" to camera, + "flashlight" to flashlight ) - private val configByClass = - configsByPosition.values.flatten().associateBy { config -> config::class } + + private var configsByPosition: Map> + private var configByClass: Map, KeyguardQuickAffordanceConfig> + + init { + configsByPosition = mapOf() + configByClass = mapOf() + updateSettings() + } override fun getAll( position: KeyguardQuickAffordancePosition, @@ -60,4 +71,35 @@ constructor( ): KeyguardQuickAffordanceConfig { return configByClass.getValue(configClass) } + + override fun updateSettings() { + var setting = Settings.System.getString(context.getContentResolver(), + Settings.System.KEYGUARD_QUICK_TOGGLES) + if (setting == null || setting.isEmpty()) + setting = "home,flashlight;wallet,qr,camera" + val split: List = setting.split(";") + val start: List = split.get(0).split(",") + val end: List = split.get(1).split(",") + var startList: MutableList = mutableListOf() + var endList: MutableList = mutableListOf() + if (!start.get(0).equals("none")) { + for (str in start) + startList.add(configsBySetting.getOrDefault(str, homeControls)) + } + if (!end.get(0).equals("none")) { + for (str in end) + endList.add(configsBySetting.getOrDefault(str, quickAccessWallet)) + } + + configsByPosition = + mapOf( + KeyguardQuickAffordancePosition.BOTTOM_START to + startList, + KeyguardQuickAffordancePosition.BOTTOM_END to + endList, + ) + + configByClass = + configsByPosition.values.flatten().associateBy { config -> config::class } + } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt index c4e3d4e4c1b5..ff3afbe384c7 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt @@ -27,7 +27,6 @@ import androidx.core.view.isVisible import androidx.core.view.updateLayoutParams import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle -import com.android.settingslib.Utils import com.android.systemui.R import com.android.systemui.animation.ActivityLaunchAnimator import com.android.systemui.animation.Interpolators @@ -241,15 +240,6 @@ object KeyguardBottomAreaViewBinder { is ContainedDrawable.WithResource -> view.setImageResource(viewModel.icon.resourceId) } - view.drawable.setTint( - Utils.getColorAttrDefaultColor( - view.context, - com.android.internal.R.attr.textColorPrimary - ) - ) - view.backgroundTintList = - Utils.getColorAttr(view.context, com.android.internal.R.attr.colorSurface) - view.contentDescription = view.context.getString(viewModel.contentDescriptionResourceId) view.isClickable = viewModel.isClickable if (viewModel.isClickable) { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt index e3ebac60febb..27dc3b4d418e 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt @@ -56,10 +56,10 @@ constructor( .distinctUntilChanged() /** An observable for the view-model of the "start button" quick affordance. */ - val startButton: Flow = + var startButton: Flow = button(KeyguardQuickAffordancePosition.BOTTOM_START) /** An observable for the view-model of the "end button" quick affordance. */ - val endButton: Flow = + var endButton: Flow = button(KeyguardQuickAffordancePosition.BOTTOM_END) /** An observable for whether the overlay container should be visible. */ val isOverlayContainerVisible: Flow = @@ -67,7 +67,7 @@ constructor( /** An observable for the alpha level for the entire bottom area. */ val alpha: Flow = bottomAreaInteractor.alpha.distinctUntilChanged() /** An observable for whether the indication area should be padded. */ - val isIndicationAreaPadded: Flow = + var isIndicationAreaPadded: Flow = combine(startButton, endButton) { startButtonModel, endButtonModel -> startButtonModel.isVisible || endButtonModel.isVisible } @@ -129,6 +129,17 @@ constructor( } } + fun updateSettings() { + quickAffordanceInteractor.updateSettings() + startButton = button(KeyguardQuickAffordancePosition.BOTTOM_START) + endButton = button(KeyguardQuickAffordancePosition.BOTTOM_END) + isIndicationAreaPadded = + combine(startButton, endButton) { startButtonModel, endButtonModel -> + startButtonModel.isVisible || endButtonModel.isVisible + } + .distinctUntilChanged() + } + companion object { // We select a value that's less than 1.0 because we want floating point math precision to // not be a factor in determining whether the affordance UI is fully opaque. The number we diff --git a/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java b/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java index a617850ef0ae..f8f784ff948c 100644 --- a/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java +++ b/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java @@ -112,7 +112,7 @@ public void run() { if (DEBUG) Log.d(mTag, "requesting AudioFocus"); int focusGain = AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK; if (mCmd.looping) { - focusGain = AudioManager.AUDIOFOCUS_GAIN; + focusGain = AudioManager.AUDIOFOCUS_GAIN_TRANSIENT; } mNotificationRampTimeMs = audioManager.getFocusRampTimeMs( focusGain, mCmd.attributes); diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java index 50a10bc0b15a..d28da80b5a98 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java @@ -708,7 +708,7 @@ public void onViewAttached() { } setDisabled2Flags(mDisabledFlags2); - initSecondaryHomeHandleForRotation(); + //initSecondaryHomeHandleForRotation(); // Unfortunately, we still need it because status bar needs LightBarController // before notifications creation. We cannot directly use getLightBarController() @@ -1601,7 +1601,7 @@ private WindowManager.LayoutParams getBarLayoutParamsForRotation(int rotation) { } private boolean canShowSecondaryHandle() { - return mNavBarMode == NAV_BAR_MODE_GESTURAL && mOrientationHandle != null; + return false; } private final Consumer mRotationWatcher = rotation -> { diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarInflaterView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarInflaterView.java index 59bb2278edfe..81aea1052481 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarInflaterView.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarInflaterView.java @@ -102,11 +102,15 @@ public class NavigationBarInflaterView extends FrameLayout private OverviewProxyService mOverviewProxyService; private int mNavBarMode = NAV_BAR_MODE_3BUTTON; + private int mHomeHandleWidthMode = 0; + public NavigationBarInflaterView(Context context, AttributeSet attrs) { super(context, attrs); createInflaters(); mOverviewProxyService = Dependency.get(OverviewProxyService.class); - mNavBarMode = Dependency.get(NavigationModeController.class).addListener(this); + final NavigationModeController controller = Dependency.get(NavigationModeController.class); + mNavBarMode = controller.addListener(this); + mHomeHandleWidthMode = controller.getNavigationHandleWidthMode(); } @VisibleForTesting @@ -151,6 +155,17 @@ public void onNavigationModeChanged(int mode) { mNavBarMode = mode; } + @Override + public void onNavigationHandleWidthModeChanged(int mode) { + if (mHomeHandleWidthMode != mode) { + mHomeHandleWidthMode = mode; + if (QuickStepContract.isGesturalMode(mNavBarMode)) { + clearViews(); + inflateLayout(getDefaultLayout()); + } + } + } + @Override protected void onDetachedFromWindow() { Dependency.get(NavigationModeController.class).removeListener(this); @@ -385,6 +400,16 @@ View createView(String buttonSpec, ViewGroup parent, LayoutInflater inflater) { v = inflater.inflate(R.layout.contextual, parent, false); } else if (HOME_HANDLE.equals(button)) { v = inflater.inflate(R.layout.home_handle, parent, false); + final ViewGroup.LayoutParams lp = v.getLayoutParams(); + if (mHomeHandleWidthMode == 1) { + lp.width = getResources().getDimensionPixelSize( + R.dimen.navigation_home_handle_width_medium); + v.setLayoutParams(lp); + } else if (mHomeHandleWidthMode == 2) { + lp.width = getResources().getDimensionPixelSize( + R.dimen.navigation_home_handle_width_long); + v.setLayoutParams(lp); + } } else if (IME_SWITCHER.equals(button)) { v = inflater.inflate(R.layout.ime_switcher, parent, false); } else if (button.startsWith(KEY)) { diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java index 97024881ca62..730a3fb02c19 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java @@ -40,6 +40,7 @@ import android.graphics.Rect; import android.os.Bundle; import android.os.RemoteException; +import android.provider.Settings; import android.util.AttributeSet; import android.util.Log; import android.util.SparseArray; @@ -84,6 +85,8 @@ import com.android.systemui.statusbar.phone.CentralSurfaces; import com.android.systemui.statusbar.phone.LightBarTransitionsController; import com.android.wm.shell.back.BackAnimation; +import com.android.systemui.tuner.TunerService; +import com.android.systemui.Dependency; import com.android.wm.shell.pip.Pip; import java.io.PrintWriter; @@ -93,10 +96,14 @@ import java.util.function.Consumer; /** */ -public class NavigationBarView extends FrameLayout { +public class NavigationBarView extends FrameLayout implements + TunerService.Tunable { final static boolean DEBUG = false; final static String TAG = "NavBarView"; + private static final String NAVBAR_STYLE = + "system:" + Settings.System.NAVBAR_STYLE; + final static boolean ALTERNATE_CAR_MODE_UI = false; private Executor mBgExecutor; @@ -1089,6 +1096,8 @@ protected void onAttachedToWindow() { mEdgeBackGestureHandler.onNavBarAttached(); requestApplyInsets(); reorient(); + final TunerService tunerService = Dependency.get(TunerService.class); + tunerService.addTunable(this, NAVBAR_STYLE); if (mRotationButtonController != null) { mRotationButtonController.registerListeners(); } @@ -1110,6 +1119,14 @@ protected void onDetachedFromWindow() { mEdgeBackGestureHandler.onNavBarDetached(); } + + @Override + public void onTuningChanged(String key, String newValue) { + if (NAVBAR_STYLE.equals(key)) { + reloadNavIcons(); + } + } + void dump(PrintWriter pw) { final Rect r = new Rect(); final Point size = new Point(); diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationModeController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationModeController.java index 63276fee811b..3556b311be60 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationModeController.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationModeController.java @@ -25,6 +25,9 @@ import android.content.om.IOverlayManager; import android.content.pm.PackageManager; import android.content.res.ApkAssets; +import android.database.ContentObserver; +import android.net.Uri; +import android.os.Handler; import android.os.PatternMatcher; import android.os.RemoteException; import android.os.ServiceManager; @@ -35,11 +38,13 @@ import com.android.systemui.Dumpable; import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dagger.qualifiers.UiBackground; import com.android.systemui.dump.DumpManager; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; +import com.android.systemui.util.settings.SecureSettings; import java.io.PrintWriter; import java.util.ArrayList; @@ -58,12 +63,14 @@ public class NavigationModeController implements Dumpable { public interface ModeChangedListener { void onNavigationModeChanged(int mode); + default void onNavigationHandleWidthModeChanged(int mode) {} } private final Context mContext; private Context mCurrentUserContext; private final IOverlayManager mOverlayManager; private final Executor mUiBgExecutor; + private final SecureSettings mSecureSettings; private ArrayList mListeners = new ArrayList<>(); @@ -100,7 +107,9 @@ public NavigationModeController(Context context, DeviceProvisionedController deviceProvisionedController, ConfigurationController configurationController, @UiBackground Executor uiBgExecutor, - DumpManager dumpManager) { + DumpManager dumpManager, + @Main Handler mainHandler, + SecureSettings secureSettings) { mContext = context; mCurrentUserContext = context; mOverlayManager = IOverlayManager.Stub.asInterface( @@ -125,6 +134,18 @@ public void onThemeChanged() { } }); + mSecureSettings = secureSettings; + mSecureSettings.registerContentObserverForUser( + Settings.Secure.GESTURE_NAVBAR_LENGTH_MODE, + new ContentObserver(mainHandler) { + @Override + public void onChange(boolean selfChange, Uri uri) { + mListeners.forEach(listener -> + listener.onNavigationHandleWidthModeChanged( + getNavigationHandleWidthMode())); + } + }, UserHandle.USER_ALL); + updateCurrentInteractionMode(false /* notify */); } @@ -132,8 +153,8 @@ public void updateCurrentInteractionMode(boolean notify) { mCurrentUserContext = getCurrentUserContext(); int mode = getCurrentInteractionMode(mCurrentUserContext); mUiBgExecutor.execute(() -> - Settings.Secure.putString(mCurrentUserContext.getContentResolver(), - Secure.NAVIGATION_MODE, String.valueOf(mode))); + mSecureSettings.putStringForUser(Secure.NAVIGATION_MODE, + String.valueOf(mode), UserHandle.USER_CURRENT)); if (DEBUG) { Log.d(TAG, "updateCurrentInteractionMode: mode=" + mode); dumpAssetPaths(mCurrentUserContext); @@ -189,6 +210,11 @@ public Context getCurrentUserContext() { } } + public int getNavigationHandleWidthMode() { + return mSecureSettings.getIntForUser(Settings.Secure.GESTURE_NAVBAR_LENGTH_MODE, + 0, UserHandle.USER_CURRENT); + } + @Override public void dump(PrintWriter pw, String[] args) { pw.println("NavigationModeController:"); diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java index 0f1338e4e872..199e4c757294 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java @@ -32,11 +32,14 @@ import android.graphics.Rect; import android.graphics.Region; import android.hardware.input.InputManager; +import android.os.AsyncTask; import android.os.Looper; import android.os.RemoteException; import android.os.SystemClock; import android.os.SystemProperties; import android.os.Trace; +import android.os.Vibrator; +import android.os.VibrationEffect; import android.provider.DeviceConfig; import android.util.DisplayMetrics; import android.util.Log; @@ -250,6 +253,9 @@ public void onPropertiesChanged(DeviceConfig.Properties properties) { private int mRightInset; private int mSysUiFlags; + private boolean mEdgeHapticEnabled; + private final Vibrator mVibrator; + // For Tf-Lite model. private BackGestureTfClassifierProvider mBackGestureTfClassifierProvider; private Map mVocab; @@ -272,6 +278,10 @@ public void onPropertiesChanged(DeviceConfig.Properties properties) { new NavigationEdgeBackPlugin.BackCallback() { @Override public void triggerBack() { + if (mEdgeHapticEnabled) { + vibrateBack(true /* Click */); + } + // Notify FalsingManager that an intentional gesture has occurred. // TODO(b/186519446): use a different method than isFalseTouch mFalsingManager.isFalseTouch(BACK_GESTURE); @@ -344,6 +354,7 @@ public void onSystemUiStateChanged(int sysUiFlags) { FeatureFlags featureFlags) { super(broadcastDispatcher); mContext = context; + mVibrator = context.getSystemService(Vibrator.class); mDisplayId = context.getDisplayId(); mMainExecutor = executor; mBackgroundExecutor = backgroundExecutor; @@ -406,6 +417,7 @@ public void updateCurrentUserResources() { Resources res = mNavigationModeController.getCurrentUserContext().getResources(); mEdgeWidthLeft = mGestureNavigationSettingsObserver.getLeftSensitivity(res); mEdgeWidthRight = mGestureNavigationSettingsObserver.getRightSensitivity(res); + mEdgeHapticEnabled = mGestureNavigationSettingsObserver.getEdgeHaptic(); mIsBackGestureAllowed = !mGestureNavigationSettingsObserver.areNavigationButtonForcedVisible(); @@ -487,6 +499,12 @@ public void onNavigationModeChanged(int mode) { updateCurrentUserResources(); } + private void vibrateBack(boolean light) { + AsyncTask.execute(() -> + mVibrator.vibrate(VibrationEffect.get(light ? VibrationEffect.EFFECT_CLICK : + VibrationEffect.EFFECT_HEAVY_CLICK, true /* fallback */))); + } + public void onNavBarTransientStateChanged(boolean isTransient) { mIsNavBarShownTransiently = isTransient; } diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java index 24efc762b39b..da226a48d85f 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java @@ -57,7 +57,7 @@ import com.android.systemui.animation.Interpolators; import com.android.systemui.plugins.NavigationEdgeBackPlugin; import com.android.systemui.shared.navigationbar.RegionSamplingHelper; -import com.android.systemui.statusbar.VibratorHelper; +//import com.android.systemui.statusbar.VibratorHelper; import java.io.PrintWriter; import java.util.concurrent.Executor; @@ -136,7 +136,7 @@ public class NavigationBarEdgePanel extends View implements NavigationEdgeBackPl = new PathInterpolator(1.0f / RUBBER_BAND_AMOUNT_APPEAR, 1.0f, 1.0f, 1.0f); private final WindowManager mWindowManager; - private final VibratorHelper mVibratorHelper; +// private final VibratorHelper mVibratorHelper; /** * The paint the arrow is drawn with @@ -286,7 +286,7 @@ public NavigationBarEdgePanel(Context context, LatencyTracker latencyTracker) { super(context); mWindowManager = context.getSystemService(WindowManager.class); - mVibratorHelper = Dependency.get(VibratorHelper.class); +// mVibratorHelper = Dependency.get(VibratorHelper.class); mDensity = context.getResources().getDisplayMetrics().density; @@ -638,11 +638,11 @@ private void triggerBack() { } mVelocityTracker.computeCurrentVelocity(1000); // Only do the extra translation if we're not already flinging - boolean isSlow = Math.abs(mVelocityTracker.getXVelocity()) < 500; - if (isSlow - || SystemClock.uptimeMillis() - mVibrationTime >= GESTURE_DURATION_FOR_CLICK_MS) { - mVibratorHelper.vibrate(VibrationEffect.EFFECT_CLICK); - } +// boolean isSlow = Math.abs(mVelocityTracker.getXVelocity()) < 500; +// if (isSlow +// || SystemClock.uptimeMillis() - mVibrationTime >= GESTURE_DURATION_FOR_CLICK_MS) { +// mVibratorHelper.vibrate(VibrationEffect.EFFECT_CLICK); +// } // Let's also snap the angle a bit if (mAngleOffset > -4) { @@ -737,8 +737,8 @@ private void handleMoveEvent(MotionEvent event) { // Apply a haptic on drag slop passed if (!mDragSlopPassed && touchTranslation > mSwipeTriggerThreshold) { mDragSlopPassed = true; - mVibratorHelper.vibrate(VibrationEffect.EFFECT_TICK); - mVibrationTime = SystemClock.uptimeMillis(); +// mVibratorHelper.vibrate(VibrationEffect.EFFECT_TICK); +// mVibrationTime = SystemClock.uptimeMillis(); // Let's show the arrow and animate it in! mDisappearAmount = 0.0f; diff --git a/packages/SystemUI/src/com/android/systemui/omni/CurrentWeatherView.java b/packages/SystemUI/src/com/android/systemui/omni/CurrentWeatherView.java new file mode 100644 index 000000000000..7359f3fb07e2 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/omni/CurrentWeatherView.java @@ -0,0 +1,272 @@ +/* +* Copyright (C) 2018 The OmniROM Project +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* +*/ +package com.android.systemui.omni; + +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.content.res.ColorStateList; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.database.ContentObserver; +import android.graphics.Bitmap; +import android.graphics.BlurMaskFilter; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.PaintFlagsDrawFilter; +import android.graphics.Rect; +import android.graphics.Typeface; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.VectorDrawable; +import androidx.core.graphics.ColorUtils; +import android.text.TextPaint; +import android.text.format.DateFormat; +import android.util.ArraySet; +import android.util.AttributeSet; +import android.util.Log; +import android.util.TypedValue; +import android.view.LayoutInflater; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.FrameLayout; +import android.widget.LinearLayout; +import android.widget.TextView; +import android.os.Handler; +import android.os.UserHandle; +import android.provider.Settings; + +import com.android.internal.util.omni.OmniJawsClient; +import com.android.systemui.R; +import com.android.settingslib.Utils; + +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; + +public class CurrentWeatherView extends FrameLayout implements OmniJawsClient.OmniJawsObserver { + + static final String TAG = "SystemUI:CurrentWeatherView"; + static final boolean DEBUG = false; + + private ImageView mCurrentImage; + private OmniJawsClient mWeatherClient; + private TextView mLeftText; + private TextView mRightText; + private int mTextColor; + private float mDarkAmount; + private boolean mUpdatesEnabled; + private SettingsObserver mSettingsObserver; + + public CurrentWeatherView(Context context) { + this(context, null); + } + + public CurrentWeatherView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public CurrentWeatherView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + public void enableUpdates() { + if (mUpdatesEnabled) { + return; + } + if (DEBUG) Log.d(TAG, "enableUpdates"); + mWeatherClient = new OmniJawsClient(getContext(), false); + setVisibility(View.VISIBLE); + if (mWeatherClient.isOmniJawsEnabled()) { + mWeatherClient.addSettingsObserver(); + mWeatherClient.addObserver(this); + queryAndUpdateWeather(); + mUpdatesEnabled = true; + } + } + + public void disableUpdates() { + if (!mUpdatesEnabled) { + return; + } + if (DEBUG) Log.d(TAG, "disableUpdates"); + setVisibility(View.GONE); + if (mWeatherClient != null) { + mWeatherClient.removeObserver(this); + mWeatherClient.cleanupObserver(); + mUpdatesEnabled = false; + } + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + mCurrentImage = (ImageView) findViewById(R.id.current_image); + mLeftText = (TextView) findViewById(R.id.left_text); + mRightText = (TextView) findViewById(R.id.right_text); + mTextColor = mLeftText.getCurrentTextColor(); + mSettingsObserver = new SettingsObserver(new Handler()); + mSettingsObserver.observe(); + mSettingsObserver.update(); + } + + private void updateWeatherData(OmniJawsClient.WeatherInfo weatherData) { + boolean showTemp = Settings.System.getIntForUser(getContext().getContentResolver(), + Settings.System.LOCKSCREEN_WEATHER_SHOW_TEMP, 1, UserHandle.USER_CURRENT) == 1; + boolean showCity = Settings.System.getIntForUser(getContext().getContentResolver(), + Settings.System.LOCKSCREEN_WEATHER_SHOW_CITY, 0, UserHandle.USER_CURRENT) == 1; + if (DEBUG) Log.d(TAG, "updateWeatherData"); + + if (!mWeatherClient.isOmniJawsEnabled()) { + setErrorView(); + return; + } + if (weatherData == null) { + setBlankView(); + return; + } + Drawable d = mWeatherClient.getWeatherConditionImage(weatherData.conditionCode); + d = d.mutate(); + updateTint(d); + mCurrentImage.setImageDrawable(d); + if (showTemp) { + mRightText.setText(weatherData.temp + " " + weatherData.tempUnits); + } else { + mRightText.setText(""); + } + if (showCity) { + mLeftText.setText(weatherData.city); + } else { + mLeftText.setText(""); + } + } + + private int getTintColor() { + return Utils.getColorAttrDefaultColor(mContext, R.attr.wallpaperTextColor); + } + + private void setErrorView() { + Drawable d = mContext.getResources().getDrawable(R.drawable.ic_qs_weather_default_off_white); + updateTint(d); + mCurrentImage.setImageDrawable(d); + mLeftText.setText(""); + mRightText.setText(""); + } + + private void setBlankView() { + mCurrentImage.setImageDrawable(null); + mLeftText.setText(""); + mRightText.setText(""); + } + + @Override + public void weatherError(int errorReason) { + if (DEBUG) Log.d(TAG, "weatherError " + errorReason); + // since this is shown in ambient and lock screen + // it would look bad to show every error since the + // screen-on revovery of the service had no chance + // to run fast enough + // so only show the disabled state + if (errorReason == OmniJawsClient.EXTRA_ERROR_DISABLED) { + setErrorView(); + } + } + + @Override + public void weatherUpdated() { + if (DEBUG) Log.d(TAG, "weatherUpdated"); + queryAndUpdateWeather(); + } + + @Override + public void updateSettings() { + if (DEBUG) Log.d(TAG, "updateSettings"); + OmniJawsClient.WeatherInfo weatherData = mWeatherClient.getWeatherInfo(); + updateWeatherData(weatherData); + } + + private void queryAndUpdateWeather() { + if (mWeatherClient == null) return; + try { + if (DEBUG) Log.d(TAG, "queryAndUpdateWeather"); + mWeatherClient.queryWeather(); + OmniJawsClient.WeatherInfo weatherData = mWeatherClient.getWeatherInfo(); + updateWeatherData(weatherData); + } catch(Exception e) { + // Do nothing + } + } + + public void blendARGB(float darkAmount) { + mDarkAmount = darkAmount; + mLeftText.setTextColor(ColorUtils.blendARGB(mTextColor, Color.WHITE, darkAmount)); + mRightText.setTextColor(ColorUtils.blendARGB(mTextColor, Color.WHITE, darkAmount)); + + if (mWeatherClient != null) { + // update image with correct tint + OmniJawsClient.WeatherInfo weatherData = mWeatherClient.getWeatherInfo(); + updateWeatherData(weatherData); + } + } + + private void updateTint(Drawable d) { + if (mDarkAmount == 1) { + mCurrentImage.setImageTintList(ColorStateList.valueOf(Color.WHITE)); + } else { + mCurrentImage.setImageTintList((d instanceof VectorDrawable) ? ColorStateList.valueOf(getTintColor()) : null); + } + } + + public void onDensityOrFontScaleChanged() { + mLeftText.setTextSize(TypedValue.COMPLEX_UNIT_PX, + getResources().getDimensionPixelSize(R.dimen.widget_label_font_size)); + mRightText.setTextSize(TypedValue.COMPLEX_UNIT_PX, + getResources().getDimensionPixelSize(R.dimen.widget_label_font_size)); + mCurrentImage.getLayoutParams().height = + getResources().getDimensionPixelSize(R.dimen.current_weather_image_size); + mCurrentImage.getLayoutParams().width = + getResources().getDimensionPixelSize(R.dimen.current_weather_image_size); + } + + private class SettingsObserver extends ContentObserver { + SettingsObserver(Handler handler) { + super(handler); + } + + void observe() { + getContext().getContentResolver().registerContentObserver(Settings.System.getUriFor( + Settings.System.LOCKSCREEN_WEATHER_SHOW_TEMP), + false, this, UserHandle.USER_ALL); + getContext().getContentResolver().registerContentObserver(Settings.System.getUriFor( + Settings.System.LOCKSCREEN_WEATHER_SHOW_CITY), + false, this, UserHandle.USER_ALL); + } + + @Override + public void onChange(boolean selfChange) { + update(); + } + + public void update() { + queryAndUpdateWeather(); + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java b/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java index be82b1faac8e..67e96645f9c5 100644 --- a/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java +++ b/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java @@ -1096,7 +1096,7 @@ CharSequence getDoubleEmoji(CharSequence message) { Pair first = emojiIndices.get(i - 1); // Check if second emoji starts right after first starts - if (second.first == first.second) { + if (Objects.equals(second.first, first.second)) { // Check if emojis in sequence are the same if (Objects.equals(emojiTexts.get(i), emojiTexts.get(i - 1))) { if (DEBUG) { diff --git a/packages/SystemUI/src/com/android/systemui/privacy/AppOpsPrivacyItemMonitor.kt b/packages/SystemUI/src/com/android/systemui/privacy/AppOpsPrivacyItemMonitor.kt index de34cd6b23ca..5b7f832afbaa 100644 --- a/packages/SystemUI/src/com/android/systemui/privacy/AppOpsPrivacyItemMonitor.kt +++ b/packages/SystemUI/src/com/android/systemui/privacy/AppOpsPrivacyItemMonitor.kt @@ -53,6 +53,9 @@ class AppOpsPrivacyItemMonitor @Inject constructor( @VisibleForTesting companion object { + val CAMERA_WHITELIST_PKG = arrayOf( + "org.pixelexperience.faceunlock", + ) val OPS_MIC_CAMERA = intArrayOf(AppOpsManager.OP_CAMERA, AppOpsManager.OP_PHONE_CALL_CAMERA, AppOpsManager.OP_RECORD_AUDIO, AppOpsManager.OP_PHONE_CALL_MICROPHONE, @@ -85,7 +88,8 @@ class AppOpsPrivacyItemMonitor @Inject constructor( ) { synchronized(lock) { // Check if we care about this code right now - if (code in OPS_MIC_CAMERA && !micCameraAvailable) { + if (code in OPS_MIC_CAMERA && !micCameraAvailable + || packageName in CAMERA_WHITELIST_PKG) { return } if (code in OPS_LOCATION && !locationAvailable) { @@ -213,6 +217,10 @@ class AppOpsPrivacyItemMonitor @Inject constructor( AppOpsManager.OP_RECORD_AUDIO -> PrivacyType.TYPE_MICROPHONE else -> return null } + if (type == PrivacyType.TYPE_CAMERA && !micCameraAvailable + || appOpItem.packageName in CAMERA_WHITELIST_PKG) { + return null + } val app = PrivacyApplication(appOpItem.packageName, appOpItem.uid) return PrivacyItem(type, app, appOpItem.timeStartedElapsed, appOpItem.isDisabled) } diff --git a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt index 2a6ca1acb38e..b1a87364cfb9 100644 --- a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt +++ b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt @@ -101,8 +101,8 @@ class OngoingPrivacyChip @JvmOverloads constructor( .getDimensionPixelSize(R.dimen.ongoing_appops_chip_icon_margin) iconSize = context.resources .getDimensionPixelSize(R.dimen.ongoing_appops_chip_icon_size) - iconColor = - Utils.getColorAttrDefaultColor(context, com.android.internal.R.attr.colorPrimary) + iconColor = context.resources + .getColor(android.R.color.system_neutral1_900) val padding = context.resources .getDimensionPixelSize(R.dimen.ongoing_appops_chip_side_padding) diff --git a/packages/SystemUI/src/com/android/systemui/qs/HeaderPrivacyIconsController.kt b/packages/SystemUI/src/com/android/systemui/qs/HeaderPrivacyIconsController.kt index dc79f40ffef6..6f645b562008 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/HeaderPrivacyIconsController.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/HeaderPrivacyIconsController.kt @@ -26,6 +26,7 @@ import java.util.concurrent.Executor import javax.inject.Inject import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.statusbar.policy.DeviceProvisionedController interface ChipVisibilityListener { fun onChipVisibilityRefreshed(visible: Boolean) @@ -54,7 +55,8 @@ class HeaderPrivacyIconsController @Inject constructor( private val activityStarter: ActivityStarter, private val appOpsController: AppOpsController, private val broadcastDispatcher: BroadcastDispatcher, - private val safetyCenterManager: SafetyCenterManager + private val safetyCenterManager: SafetyCenterManager, + private val deviceProvisionedController: DeviceProvisionedController ) { var chipVisibilityListener: ChipVisibilityListener? = null @@ -134,6 +136,8 @@ class HeaderPrivacyIconsController @Inject constructor( fun onParentVisible() { privacyChip.setOnClickListener { + // Do not expand dialog while device is not provisioned + if (!deviceProvisionedController.isDeviceProvisioned) return@setOnClickListener // If the privacy chip is visible, it means there were some indicators uiEventLogger.log(PrivacyChipEvent.ONGOING_INDICATORS_CHIP_CLICK) if (safetyCenterEnabled) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java index 0697133a02f9..cffaffcaa2f9 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java +++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java @@ -123,12 +123,16 @@ public void restoreInstanceState(Bundle savedInstanceState) { @Override public int getTilesHeight() { - // Use the first page as that is the maximum height we need to show. - TileLayout tileLayout = mPages.get(0); - if (tileLayout == null) { - return 0; + // Find the maximum height among all pages. + int height = 0; + for (int i = 0; i < mPages.size(); i++) { + TileLayout tileLayout = mPages.get(i); + if (tileLayout != null) { + height = Math.max(height, tileLayout.getTilesHeight()); + } } - return tileLayout.getTilesHeight(); + if (DEBUG) Log.d(TAG, "getTilesHeight ret=" + height); + return height; } @Override diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java index e1289a61d45d..e19fa4a9d577 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java @@ -44,6 +44,9 @@ import javax.inject.Inject; +import kotlin.Unit; +import kotlin.jvm.functions.Function1; + /** * Performs the animated transition between the QQS and QS views. * @@ -137,12 +140,18 @@ public class QSAnimator implements QSHost.Callback, PagedTileLayout.PageListener private float mLastPosition; private final QSTileHost mHost; private final Executor mExecutor; + private final TunerService mTunerService; private boolean mShowCollapsedOnKeyguard; private int mQQSTop; private int[] mTmpLoc1 = new int[2]; private int[] mTmpLoc2 = new int[2]; + private final Function1 mMediaHostVisibilityListener = (visible) -> { + requestAnimatorUpdate(); + return null; + }; + @Inject public QSAnimator(QS qs, QuickQSPanel quickPanel, QuickStatusBarHeader quickStatusBarHeader, QSPanelController qsPanelController, @@ -156,6 +165,7 @@ public QSAnimator(QS qs, QuickQSPanel quickPanel, QuickStatusBarHeader quickStat mQuickStatusBarHeader = quickStatusBarHeader; mHost = qsTileHost; mExecutor = executor; + mTunerService = tunerService; mQSExpansionPathInterpolator = qsExpansionPathInterpolator; mHost.addCallback(this); mQsPanelController.addOnAttachStateChangeListener(this); @@ -213,11 +223,13 @@ private void updateQQSVisibility() { @Override public void onViewAttachedToWindow(@NonNull View view) { updateAnimators(); + mQuickQSPanelController.mMediaHost.addVisibilityChangeListener(mMediaHostVisibilityListener); } @Override public void onViewDetachedFromWindow(@NonNull View v) { mHost.removeCallback(this); + mQuickQSPanelController.mMediaHost.removeVisibilityChangeListener(mMediaHostVisibilityListener); } private void addNonFirstPageAnimators(int page) { @@ -561,6 +573,12 @@ private void animateBrightnessSlider() { mBrightnessOpacityAnimator = null; View qsBrightness = mQsPanelController.getBrightnessView(); View qqsBrightness = mQuickQSPanelController.getBrightnessView(); + + if (mTunerService.getValue(QSPanel.QS_SHOW_BRIGHTNESS_SLIDER, 2) == 0) { + qsBrightness.setVisibility(View.GONE); + qqsBrightness.setVisibility(View.GONE); + } + if (qqsBrightness != null && qqsBrightness.getVisibility() == View.VISIBLE) { // animating in split shade mode mAnimatedQsViews.add(qsBrightness); @@ -573,6 +591,8 @@ private void animateBrightnessSlider() { .addFloat(qsBrightness, "sliderScaleY", 0.3f, 1) .addFloat(qqsBrightness, "translationY", 0, translationY) .setInterpolator(mQSExpansionPathInterpolator.getYInterpolator()) + .setInterpolator(mQuickQSPanelController.mMediaHost.getVisible() ? + Interpolators.ALPHA_OUT : Interpolators.SLOWDOWN) .build(); } else if (qsBrightness != null) { // The brightness slider's visible bottom edge must maintain a constant margin from the diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java index 61905ae00be8..f7953aea7747 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java @@ -21,12 +21,23 @@ import android.content.Context; import android.content.res.Configuration; import android.database.ContentObserver; +import android.net.ConnectivityManager; +import android.net.Network; +import android.net.NetworkCapabilities; import android.net.Uri; +import android.net.wifi.WifiInfo; +import android.net.wifi.WifiManager; import android.os.Build; import android.os.Handler; import android.os.UserHandle; import android.provider.Settings; +import android.telephony.SubscriptionInfo; +import android.telephony.SubscriptionManager; +import android.text.BidiFormatter; +import android.text.format.Formatter; +import android.text.format.Formatter.BytesResult; import android.util.AttributeSet; +import android.util.Log; import android.view.View; import android.widget.FrameLayout; import android.widget.TextView; @@ -35,15 +46,20 @@ import androidx.annotation.VisibleForTesting; import com.android.settingslib.development.DevelopmentSettingsEnabler; +import com.android.settingslib.net.DataUsageController; import com.android.systemui.R; +import java.util.List; + /** * Footer of expanded Quick Settings, tiles page indicator, (optionally) build number and * {@link FooterActionsView} */ public class QSFooterView extends FrameLayout { + private static final String TAG = "QSFooterView"; + private PageIndicator mPageIndicator; - private TextView mBuildText; + private TextView mUsageText; private View mEditButton; @Nullable @@ -53,51 +69,147 @@ public class QSFooterView extends FrameLayout { private boolean mExpanded; private float mExpansionAmount; - private boolean mShouldShowBuildText; + private boolean mHideDataUsage; + private boolean mShouldShowUsageText; + private boolean mShouldShowSuffix; + private boolean mForceShowSuffix; @Nullable private OnClickListener mExpandClickListener; - private final ContentObserver mDeveloperSettingsObserver = new ContentObserver( - new Handler(mContext.getMainLooper())) { - @Override - public void onChange(boolean selfChange, Uri uri) { - super.onChange(selfChange, uri); - setBuildText(); - } - }; + private DataUsageController mDataController; + private SubscriptionManager mSubManager; + + private boolean mHasNoSims; + private boolean mIsWifiConnected; + private String mWifiSsid; public QSFooterView(Context context, AttributeSet attrs) { super(context, attrs); + mDataController = new DataUsageController(context); + mSubManager = (SubscriptionManager) context.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE); } @Override protected void onFinishInflate() { super.onFinishInflate(); mPageIndicator = findViewById(R.id.footer_page_indicator); - mBuildText = findViewById(R.id.build); + mUsageText = findViewById(R.id.build); mEditButton = findViewById(android.R.id.edit); updateResources(); setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES); - setBuildText(); - } - - private void setBuildText() { - if (mBuildText == null) return; - if (DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(mContext)) { - mBuildText.setText(mContext.getString( - com.android.internal.R.string.bugreport_status, - Build.VERSION.RELEASE_OR_CODENAME, - Build.ID)); - // Set as selected for marquee before its made visible, then it won't be announced when - // it's made visible. - mBuildText.setSelected(true); - mShouldShowBuildText = true; + setClickable(false); + setUsageText(); + + mUsageText.setOnClickListener(v -> { + mForceShowSuffix = !mForceShowSuffix; + setUsageText(); + }); + } + + private void setUsageText() { + if (mUsageText == null || mHideDataUsage || !mExpanded) return; + DataUsageController.DataUsageInfo info; + String suffix; + if (mIsWifiConnected) { + info = mDataController.getWifiDailyDataUsageInfo(true); + if (info == null) { + info = mDataController.getWifiDailyDataUsageInfo(false); + suffix = mContext.getResources().getString(R.string.usage_wifi_default_suffix); + } else { + suffix = getWifiSsid(); + } + } else if (!mHasNoSims) { + mDataController.setSubscriptionId( + SubscriptionManager.getDefaultDataSubscriptionId()); + info = mDataController.getDailyDataUsageInfo(); + suffix = getSlotCarrierName(); + } else { + mShouldShowUsageText = false; + mUsageText.setText(null); + updateVisibilities(); + return; + } + if (info == null) { + Log.w(TAG, "setUsageText: DataUsageInfo is NULL."); + return; + } + mShouldShowUsageText = true; + mUsageText.setText(formatDataUsage(info.usageLevel, suffix)); + updateVisibilities(); + } + + private CharSequence formatDataUsage(long byteValue, String suffix) { + final BytesResult res = Formatter.formatBytes(mContext.getResources(), byteValue, + Formatter.FLAG_IEC_UNITS); + // Example: 1.23 GB used today + String usage = BidiFormatter.getInstance().unicodeWrap(mContext.getString( + com.android.internal.R.string.fileSizeSuffix, res.value, res.units)) + + " " + mContext.getString(R.string.usage_data); + if (mShouldShowSuffix ^ mForceShowSuffix) { + // Example: 1.23 GB used today (airtel) + usage += " (" + suffix + ")"; + } + return usage; + } + + private String getSlotCarrierName() { + CharSequence result = mContext.getResources().getString(R.string.usage_data_default_suffix); + int subId = mSubManager.getDefaultDataSubscriptionId(); + final List subInfoList = + mSubManager.getActiveSubscriptionInfoList(true); + if (subInfoList != null) { + for (SubscriptionInfo subInfo : subInfoList) { + if (subId == subInfo.getSubscriptionId()) { + result = subInfo.getDisplayName(); + break; + } + } + } + return result.toString(); + } + + private String getWifiSsid() { + if (mWifiSsid == null) { + return mContext.getResources().getString(R.string.usage_wifi_default_suffix); } else { - mBuildText.setText(null); - mShouldShowBuildText = false; - mBuildText.setSelected(false); + return mWifiSsid.replace("\"", ""); + } + } + + protected void setWifiSsid(String ssid) { + if (mWifiSsid != ssid) { + mWifiSsid = ssid; + setUsageText(); + } + } + + protected void setIsWifiConnected(boolean connected) { + if (mIsWifiConnected != connected) { + mIsWifiConnected = connected; + setUsageText(); + } + } + + protected void setNoSims(boolean hasNoSims) { + if (mHasNoSims != hasNoSims) { + mHasNoSims = hasNoSims; + setUsageText(); + } + } + + protected void setShowSuffix(boolean show) { + if (mShouldShowSuffix != show) { + mShouldShowSuffix = show; + setUsageText(); + } + } + + protected void setHideDataUsage(boolean hide) { + if (mHideDataUsage != hide) { + mHideDataUsage = hide; + updateVisibilities(); } } @@ -122,7 +234,7 @@ private void updateFooterAnimator() { private TouchAnimator createFooterAnimator() { TouchAnimator.Builder builder = new TouchAnimator.Builder() .addFloat(mPageIndicator, "alpha", 0, 1) - .addFloat(mBuildText, "alpha", 0, 1) + .addFloat(mUsageText, "alpha", 0, 1) .addFloat(mEditButton, "alpha", 0, 1) .setStartDelay(0.9f); return builder.build(); @@ -149,21 +261,14 @@ public void setExpansion(float headerExpansionFraction) { if (mFooterAnimator != null) { mFooterAnimator.setPosition(headerExpansionFraction); } - } - @Override - protected void onAttachedToWindow() { - super.onAttachedToWindow(); - mContext.getContentResolver().registerContentObserver( - Settings.Global.getUriFor(Settings.Global.DEVELOPMENT_SETTINGS_ENABLED), false, - mDeveloperSettingsObserver, UserHandle.USER_ALL); - } - - @Override - @VisibleForTesting - public void onDetachedFromWindow() { - mContext.getContentResolver().unregisterContentObserver(mDeveloperSettingsObserver); - super.onDetachedFromWindow(); + if (mUsageText == null || mHideDataUsage) return; + if (headerExpansionFraction == 1.0f) { + postDelayed(() -> mUsageText.setSelected(true), 1000); + } else if (headerExpansionFraction == 0.0f) { + mUsageText.setSelected(false); + mForceShowSuffix = false; + } } void disable(int state2) { @@ -176,16 +281,12 @@ void disable(int state2) { void updateEverything() { post(() -> { updateVisibilities(); - updateClickabilities(); - setClickable(false); + setUsageText(); }); } - private void updateClickabilities() { - mBuildText.setLongClickable(mBuildText.getVisibility() == View.VISIBLE); - } - private void updateVisibilities() { - mBuildText.setVisibility(mExpanded && mShouldShowBuildText ? View.VISIBLE : View.INVISIBLE); + mUsageText.setVisibility(!mHideDataUsage && mExpanded && mShouldShowUsageText + ? View.VISIBLE : View.INVISIBLE); } -} \ No newline at end of file +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java index 0d29a1abca9f..fb4b2ba0e97c 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java @@ -16,8 +16,18 @@ package com.android.systemui.qs; +import static android.provider.Settings.Secure.QS_SHOW_DATA_USAGE; +import static android.provider.Settings.Secure.QS_TILES; + +import android.content.BroadcastReceiver; +import android.content.Context; import android.content.ClipData; import android.content.ClipboardManager; +import android.content.Intent; +import android.content.IntentFilter; +import android.net.ConnectivityManager; +import android.net.NetworkScoreManager; +import android.net.wifi.WifiManager; import android.text.TextUtils; import android.view.View; import android.widget.TextView; @@ -28,57 +38,78 @@ import com.android.systemui.plugins.FalsingManager; import com.android.systemui.qs.dagger.QSScope; import com.android.systemui.settings.UserTracker; +import com.android.systemui.statusbar.connectivity.NetworkController; +import com.android.systemui.statusbar.connectivity.SignalCallback; +import com.android.systemui.tuner.TunerService; import com.android.systemui.util.ViewController; +import com.android.settingslib.wifi.WifiStatusTracker; + +import java.util.Arrays; import javax.inject.Inject; /** * Controller for {@link QSFooterView}. */ @QSScope -public class QSFooterViewController extends ViewController implements QSFooter { +public class QSFooterViewController extends ViewController + implements QSFooter, TunerService.Tunable { private final UserTracker mUserTracker; private final QSPanelController mQsPanelController; - private final TextView mBuildText; private final PageIndicator mPageIndicator; private final View mEditButton; private final FalsingManager mFalsingManager; private final ActivityStarter mActivityStarter; + private final WifiStatusTracker mWifiTracker; + private final NetworkController mNetworkController; + private final Context mContext; + private final TunerService mTunerService; + + private static final String INTERNET_TILE = "internet"; + + private final BroadcastReceiver mReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + mWifiTracker.handleBroadcast(intent); + onWifiStatusUpdated(); + } + }; + + private final SignalCallback mSignalCallback = new SignalCallback() { + @Override + public void setNoSims(boolean show, boolean simDetected) { + mView.setNoSims(show); + } + }; @Inject QSFooterViewController(QSFooterView view, UserTracker userTracker, FalsingManager falsingManager, ActivityStarter activityStarter, - QSPanelController qsPanelController) { + QSPanelController qsPanelController, + NetworkController networkController, + Context context, + TunerService tunerService) { super(view); mUserTracker = userTracker; mQsPanelController = qsPanelController; mFalsingManager = falsingManager; mActivityStarter = activityStarter; - - mBuildText = mView.findViewById(R.id.build); + mNetworkController = networkController; + mContext = context; + mTunerService = tunerService; mPageIndicator = mView.findViewById(R.id.footer_page_indicator); mEditButton = mView.findViewById(android.R.id.edit); + mWifiTracker = new WifiStatusTracker(context, context.getSystemService(WifiManager.class), + context.getSystemService(NetworkScoreManager.class), + context.getSystemService(ConnectivityManager.class), + this::onWifiStatusUpdated); } @Override protected void onViewAttached() { - mBuildText.setOnLongClickListener(view -> { - CharSequence buildText = mBuildText.getText(); - if (!TextUtils.isEmpty(buildText)) { - ClipboardManager service = - mUserTracker.getUserContext().getSystemService(ClipboardManager.class); - String label = getResources().getString(R.string.build_number_clip_data_label); - service.setPrimaryClip(ClipData.newPlainText(label, buildText)); - Toast.makeText(getContext(), R.string.build_number_copy_toast, Toast.LENGTH_SHORT) - .show(); - return true; - } - return false; - }); - mEditButton.setOnClickListener(view -> { if (mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) { return; @@ -87,11 +118,41 @@ protected void onViewAttached() { .postQSRunnableDismissingKeyguard(() -> mQsPanelController.showEdit(view)); }); mQsPanelController.setFooterPageIndicator(mPageIndicator); - mView.updateEverything(); + final IntentFilter filter = new IntentFilter(); + filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION); + filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION); + filter.addAction(WifiManager.RSSI_CHANGED_ACTION); + mContext.registerReceiver(mReceiver, filter); + mWifiTracker.fetchInitialState(); + mWifiTracker.setListening(true); + onWifiStatusUpdated(); + mNetworkController.addCallback(mSignalCallback); + mTunerService.addTunable(this, QS_TILES, QS_SHOW_DATA_USAGE); } @Override - protected void onViewDetached() {} + protected void onViewDetached() { + mContext.unregisterReceiver(mReceiver); + mNetworkController.removeCallback(mSignalCallback); + mTunerService.removeTunable(this); + } + + @Override + public void onTuningChanged(String key, String newValue) { + if (key.equals(QS_TILES)) { + if (TextUtils.isEmpty(newValue)) { + newValue = mContext.getString(R.string.quick_settings_tiles_default); + } + int rows = mContext.getResources().getInteger(R.integer.quick_settings_max_rows); + int cols = mContext.getResources().getInteger(R.integer.quick_settings_num_columns); + // Don't show the suffix if we have internet tile in the first page. + mView.setShowSuffix(!Arrays.stream(newValue.split(",")) + .limit(rows * cols) + .anyMatch(INTERNET_TILE::equals)); + } else if (key.equals(QS_SHOW_DATA_USAGE)) { + mView.setHideDataUsage(!TunerService.parseIntegerSwitch(newValue, true)); + } + } @Override public void setVisibility(int visibility) { @@ -118,4 +179,9 @@ public void setKeyguardShowing(boolean keyguardShowing) { public void disable(int state1, int state2, boolean animate) { mView.disable(state2); } + + private void onWifiStatusUpdated() { + mView.setIsWifiConnected(mWifiTracker.connected); + mView.setWifiSsid(mWifiTracker.ssid); + } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java index 7b27cf45979f..c2306f1d2d4e 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java @@ -499,6 +499,7 @@ public QSPanelController getQSPanelController() { public void setBrightnessMirrorController( BrightnessMirrorController brightnessMirrorController) { mQSPanelController.setBrightnessMirror(brightnessMirrorController); + mQuickQSPanelController.setBrightnessMirror(brightnessMirrorController); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java index 184089f7eef4..d681c7bb094d 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java @@ -27,6 +27,8 @@ import android.os.Bundle; import android.os.Handler; import android.os.Message; +import android.os.UserHandle; +import android.provider.Settings; import android.util.ArrayMap; import android.util.AttributeSet; import android.util.Log; @@ -41,6 +43,7 @@ import com.android.internal.logging.UiEventLogger; import com.android.internal.widget.RemeasuringLinearLayout; +import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.plugins.qs.QSTile; import com.android.systemui.settings.brightness.BrightnessSliderController; @@ -53,8 +56,12 @@ /** View that represents the quick settings tile panel (when expanded/pulled down). **/ public class QSPanel extends LinearLayout implements Tunable { - public static final String QS_SHOW_BRIGHTNESS = "qs_show_brightness"; - public static final String QS_SHOW_HEADER = "qs_show_header"; + public static final String QS_SHOW_AUTO_BRIGHTNESS = + Settings.Secure.QS_SHOW_AUTO_BRIGHTNESS; + public static final String QS_SHOW_BRIGHTNESS_SLIDER = + Settings.Secure.QS_SHOW_BRIGHTNESS_SLIDER; + public static final String QS_BRIGHTNESS_SLIDER_POSITION = + Settings.Secure.QS_BRIGHTNESS_SLIDER_POSITION; private static final String TAG = "QSPanel"; @@ -71,14 +78,21 @@ public class QSPanel extends LinearLayout implements Tunable { @Nullable protected View mBrightnessView; + protected View mAutoBrightnessView; + @Nullable protected BrightnessSliderController mToggleSliderController; + protected Runnable mBrightnessRunnable; + + protected boolean mTop; + /** Whether or not the QS media player feature is enabled. */ protected boolean mUsingMediaPlayer; protected boolean mExpanded; protected boolean mListening; + protected boolean mIsAutomaticBrightnessAvailable = false; @Nullable protected QSTileHost mHost; private final List mOnConfigurationChangedListeners = @@ -105,6 +119,7 @@ public class QSPanel extends LinearLayout implements Tunable { private final Rect mClippingRect = new Rect(); private ViewGroup mMediaHostView; private boolean mShouldMoveMediaOnExpansion = true; + private boolean mUsingCombinedHeaders = false; public QSPanel(Context context, AttributeSet attrs) { super(context, attrs); @@ -119,6 +134,11 @@ public QSPanel(Context context, AttributeSet attrs) { mMovableContentStartIndex = getChildCount(); + mIsAutomaticBrightnessAvailable = getResources().getBoolean( + com.android.internal.R.bool.config_automatic_brightness_available); + + TunerService tunerService = Dependency.get(TunerService.class); + mTop = tunerService.getValue(QS_BRIGHTNESS_SLIDER_POSITION, 1) == 0; } void initialize() { @@ -148,6 +168,10 @@ void initialize() { } } + void setUsingCombinedHeaders(boolean usingCombinedHeaders) { + mUsingCombinedHeaders = usingCombinedHeaders; + } + protected void setHorizontalContentContainerClipping() { mHorizontalContentContainer.setClipChildren(true); mHorizontalContentContainer.setClipToPadding(false); @@ -177,21 +201,28 @@ public void setBrightnessView(@NonNull View view) { removeView(mBrightnessView); mMovableContentStartIndex--; } - addView(view, 0); mBrightnessView = view; - - setBrightnessViewMargin(); - - mMovableContentStartIndex++; + mAutoBrightnessView = view.findViewById(R.id.brightness_icon); + setBrightnessViewMargin(mTop); + if (mBrightnessView != null) { + addView(mBrightnessView); + mMovableContentStartIndex++; + } } - private void setBrightnessViewMargin() { + private void setBrightnessViewMargin(boolean top) { if (mBrightnessView != null) { MarginLayoutParams lp = (MarginLayoutParams) mBrightnessView.getLayoutParams(); - lp.topMargin = mContext.getResources() - .getDimensionPixelSize(R.dimen.qs_brightness_margin_top); - lp.bottomMargin = mContext.getResources() - .getDimensionPixelSize(R.dimen.qs_brightness_margin_bottom); + if (top) { + lp.topMargin = mContext.getResources() + .getDimensionPixelSize(R.dimen.qs_top_brightness_margin_top); + lp.bottomMargin = mContext.getResources() + .getDimensionPixelSize(R.dimen.qs_top_brightness_margin_bottom); + } else { + lp.topMargin = mContext.getResources() + .getDimensionPixelSize(R.dimen.qs_bottom_brightness_margin_top); + lp.bottomMargin = 0; + } mBrightnessView.setLayoutParams(lp); } } @@ -315,13 +346,31 @@ protected String getDumpableTag() { @Override public void onTuningChanged(String key, String newValue) { - if (QS_SHOW_BRIGHTNESS.equals(key) && mBrightnessView != null) { - updateViewVisibilityForTuningValue(mBrightnessView, newValue); - } + switch (key) { + case QS_SHOW_BRIGHTNESS_SLIDER: + boolean value = + TunerService.parseInteger(newValue, 2) >= 1; + if (mBrightnessView != null) { + mBrightnessView.setVisibility(value ? VISIBLE : GONE); + } + break; + case QS_BRIGHTNESS_SLIDER_POSITION: + mTop = TunerService.parseInteger(newValue, 1) == 0; + updateBrightnessSliderPosition(); + break; + case QS_SHOW_AUTO_BRIGHTNESS: + if (mAutoBrightnessView != null) { + mAutoBrightnessView.setVisibility(mIsAutomaticBrightnessAvailable && + TunerService.parseIntegerSwitch(newValue, true) ? View.VISIBLE : View.GONE); + } + break; + default: + break; + } } - private void updateViewVisibilityForTuningValue(View view, @Nullable String newValue) { - view.setVisibility(TunerService.parseIntegerSwitch(newValue, true) ? VISIBLE : GONE); + public void setBrightnessRunnable(Runnable runnable) { + mBrightnessRunnable = runnable; } @@ -362,7 +411,7 @@ public void updateResources() { updatePageIndicator(); - setBrightnessViewMargin(); + setBrightnessViewMargin(mTop); if (mTileLayout != null) { mTileLayout.updateResources(); @@ -371,7 +420,9 @@ public void updateResources() { protected void updatePadding() { final Resources res = mContext.getResources(); - int paddingTop = res.getDimensionPixelSize(R.dimen.qs_panel_padding_top); + int paddingTop = res.getDimensionPixelSize( + mUsingCombinedHeaders ? R.dimen.qs_panel_padding_top_combined_headers + : R.dimen.qs_panel_padding_top); int paddingBottom = res.getDimensionPixelSize(R.dimen.qs_panel_padding_bottom); setPaddingRelative(getPaddingStart(), paddingTop, @@ -432,10 +483,20 @@ private boolean needsDynamicRowsAndColumns() { private void switchAllContentToParent(ViewGroup parent, QSTileLayout newLayout) { int index = parent == this ? mMovableContentStartIndex : 0; + if (mBrightnessView != null && mTop) { + switchToParent(mBrightnessView, parent, index); + index++; + } + // Let's first move the tileLayout to the new parent, since that should come first. switchToParent((View) newLayout, parent, index); index++; + if (mBrightnessView != null && !mTop) { + switchToParent(mBrightnessView, parent, index); + index++; + } + if (mFooter != null) { // Then the footer with the settings switchToParent(mFooter, parent, index); @@ -607,12 +668,17 @@ void setUsingHorizontalLayout(boolean horizontal, ViewGroup mediaHostView, boole mUsingHorizontalLayout = horizontal; ViewGroup newParent = horizontal ? mHorizontalContentContainer : this; switchAllContentToParent(newParent, mTileLayout); + if (mBrightnessRunnable != null) { + updateResources(); + mBrightnessRunnable.run(); + } reAttachMediaHost(mediaHostView, horizontal); if (needsDynamicRowsAndColumns()) { mTileLayout.setMinRows(horizontal ? 2 : 1); mTileLayout.setMaxColumns(horizontal ? 2 : 4); } updateMargins(mediaHostView); + if (mHorizontalLinearLayout == null) return; mHorizontalLinearLayout.setVisibility(horizontal ? View.VISIBLE : View.GONE); } } @@ -658,6 +724,16 @@ public void setCollapseExpandAction(Runnable action) { mCollapseExpandAction = action; } + protected void updateBrightnessSliderPosition() { + if (mBrightnessView == null) return; + ViewGroup newParent = mUsingHorizontalLayout ? mHorizontalContentContainer : this; + switchAllContentToParent(newParent, mTileLayout); + if (mBrightnessRunnable != null) { + updateResources(); + mBrightnessRunnable.run(); + } + } + private class H extends Handler { private static final int ANNOUNCE_FOR_ACCESSIBILITY = 1; diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java index 18bd6b7b3c32..7a98625199f3 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java @@ -17,8 +17,8 @@ package com.android.systemui.qs; import static com.android.systemui.classifier.Classifier.QS_SWIPE_SIDE; +import static com.android.systemui.flags.Flags.COMBINED_QS_HEADERS; import static com.android.systemui.media.dagger.MediaModule.QS_PANEL; -import static com.android.systemui.qs.QSPanel.QS_SHOW_BRIGHTNESS; import static com.android.systemui.qs.dagger.QSFragmentModule.QS_USING_MEDIA_PLAYER; import android.view.MotionEvent; @@ -27,6 +27,7 @@ import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.UiEventLogger; import com.android.systemui.dump.DumpManager; +import com.android.systemui.flags.FeatureFlags; import com.android.systemui.media.MediaHierarchyManager; import com.android.systemui.media.MediaHost; import com.android.systemui.media.MediaHostState; @@ -58,6 +59,7 @@ public class QSPanelController extends QSPanelControllerBase { private final BrightnessSliderController mBrightnessSliderController; private final BrightnessMirrorHandler mBrightnessMirrorHandler; private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; + private BrightnessMirrorController mBrightnessMirrorController; private View.OnTouchListener mTileLayoutTouchListener = new View.OnTouchListener() { @Override @@ -79,7 +81,8 @@ public boolean onTouch(View v, MotionEvent event) { QSLogger qsLogger, BrightnessController.Factory brightnessControllerFactory, BrightnessSliderController.Factory brightnessSliderFactory, FalsingManager falsingManager, - StatusBarKeyguardViewManager statusBarKeyguardViewManager) { + StatusBarKeyguardViewManager statusBarKeyguardViewManager, + FeatureFlags featureFlags) { super(view, qstileHost, qsCustomizerController, usingMediaPlayer, mediaHost, metricsLogger, uiEventLogger, qsLogger, dumpManager); mTunerService = tunerService; @@ -93,6 +96,7 @@ public boolean onTouch(View v, MotionEvent event) { mBrightnessController = brightnessControllerFactory.create(mBrightnessSliderController); mBrightnessMirrorHandler = new BrightnessMirrorHandler(mBrightnessController); mStatusBarKeyguardViewManager = statusBarKeyguardViewManager; + mView.setUsingCombinedHeaders(featureFlags.isEnabled(COMBINED_QS_HEADERS)); } @Override @@ -111,7 +115,15 @@ protected void onViewAttached() { updateMediaDisappearParameters(); - mTunerService.addTunable(mView, QS_SHOW_BRIGHTNESS); + mTunerService.addTunable(mView, QSPanel.QS_SHOW_BRIGHTNESS_SLIDER); + mTunerService.addTunable(mView, QSPanel.QS_SHOW_AUTO_BRIGHTNESS); + mTunerService.addTunable(mView, QSPanel.QS_BRIGHTNESS_SLIDER_POSITION); + + mView.setBrightnessRunnable(() -> { + mView.updateResources(); + updateBrightnessMirror(); + }); + mView.updateResources(); if (mView.isListening()) { refreshAllTiles(); @@ -132,6 +144,7 @@ protected QSTileRevealController createTileRevealController() { @Override protected void onViewDetached() { mTunerService.removeTunable(mView); + mView.setBrightnessRunnable(null); mBrightnessMirrorHandler.onQsPanelDettached(); super.onViewDetached(); } @@ -144,6 +157,12 @@ protected void onConfigurationChanged() { } } + private void updateBrightnessMirror() { + if (mBrightnessMirrorController != null) { + mBrightnessSliderController.setMirrorControllerAndMirror(mBrightnessMirrorController); + } + } + /** */ public void setVisibility(int visibility) { mView.setVisibility(visibility); @@ -163,6 +182,7 @@ public void setListening(boolean listening, boolean expanded) { } public void setBrightnessMirror(BrightnessMirrorController brightnessMirrorController) { + mBrightnessMirrorController = brightnessMirrorController; mBrightnessMirrorHandler.setController(brightnessMirrorController); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java index ded466a0cb25..2727c83ad877 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java @@ -23,8 +23,8 @@ import android.annotation.Nullable; import android.content.ComponentName; import android.content.res.Configuration; +import android.content.res.Configuration.Orientation; import android.metrics.LogMaker; -import android.util.Log; import android.view.View; import com.android.internal.annotations.VisibleForTesting; @@ -75,6 +75,7 @@ public abstract class QSPanelControllerBase extends ViewContr @Nullable private Consumer mMediaVisibilityChangedListener; + @Orientation private int mLastOrientation; private String mCachedSpecs = ""; @Nullable @@ -88,21 +89,16 @@ public abstract class QSPanelControllerBase extends ViewContr new QSPanel.OnConfigurationChangedListener() { @Override public void onConfigurationChange(Configuration newConfig) { + mQSLogger.logOnConfigurationChanged( + /* lastOrientation= */ mLastOrientation, + /* newOrientation= */ newConfig.orientation, + /* containerName= */ mView.getDumpableTag()); + mShouldUseSplitNotificationShade = - LargeScreenUtils.shouldUseSplitNotificationShade(getResources()); - // Logging to aid the investigation of b/216244185. - Log.d(TAG, - "onConfigurationChange: " - + "mShouldUseSplitNotificationShade=" - + mShouldUseSplitNotificationShade + ", " - + "newConfig.windowConfiguration=" - + newConfig.windowConfiguration); - mQSLogger.logOnConfigurationChanged(mLastOrientation, newConfig.orientation, - mView.getDumpableTag()); - if (newConfig.orientation != mLastOrientation) { - mLastOrientation = newConfig.orientation; - switchTileLayout(false); - } + LargeScreenUtils.shouldUseSplitNotificationShade(getResources()); + mLastOrientation = newConfig.orientation; + + switchTileLayoutIfNeeded(); onConfigurationChanged(); } }; @@ -334,6 +330,10 @@ void setListening(boolean listening) { } } + private void switchTileLayoutIfNeeded() { + switchTileLayout(/* force= */ false); + } + boolean switchTileLayout(boolean force) { /* Whether or not the panel currently contains a media player. */ boolean horizontal = shouldUseHorizontalLayout(); diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java index 46724adee848..dd431eb86715 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java @@ -16,6 +16,7 @@ package com.android.systemui.qs; +import android.annotation.NonNull; import android.content.Context; import android.content.res.Configuration; import android.util.AttributeSet; @@ -24,15 +25,17 @@ import android.widget.LinearLayout; import com.android.internal.logging.UiEventLogger; +import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.plugins.qs.QSTile; import com.android.systemui.plugins.qs.QSTile.SignalState; import com.android.systemui.plugins.qs.QSTile.State; +import com.android.systemui.tuner.TunerService; /** * Version of QSPanel that only shows N Quick Tiles in the QS Header. */ -public class QuickQSPanel extends QSPanel { +public class QuickQSPanel extends QSPanel implements TunerService.Tunable { private static final String TAG = "QuickQSPanel"; // A fallback value for max tiles number when setting via Tuner (parseNumTiles) @@ -52,6 +55,54 @@ protected void setHorizontalContentContainerClipping() { mHorizontalContentContainer.setClipChildren(false); } + + @Override + public void setBrightnessView(@NonNull View view) { + if (mBrightnessView != null) { + removeView(mBrightnessView); + } + mBrightnessView = view; + mAutoBrightnessView = view.findViewById(R.id.brightness_icon); + setBrightnessViewMargin(mTop); + if (mBrightnessView != null) { + addView(mBrightnessView); + + TunerService tunerService = Dependency.get(TunerService.class); + if (tunerService.getValue(QS_SHOW_BRIGHTNESS_SLIDER, 2) > 1) { + mBrightnessView.setVisibility(VISIBLE); + } + } + } + + View getBrightnessView() { + return mBrightnessView; + } + + private void setBrightnessViewMargin(boolean top) { + if (mBrightnessView != null) { + MarginLayoutParams lp = (MarginLayoutParams) mBrightnessView.getLayoutParams(); + if (top) { + lp.topMargin = mContext.getResources() + .getDimensionPixelSize(R.dimen.qqs_top_brightness_margin_top); + lp.bottomMargin = mContext.getResources() + .getDimensionPixelSize(R.dimen.qqs_top_brightness_margin_bottom); + } else { + lp.topMargin = mContext.getResources() + .getDimensionPixelSize(R.dimen.qqs_bottom_brightness_margin_top); + lp.bottomMargin = 0; + } + mBrightnessView.setLayoutParams(lp); + } + } + + @Override + void initialize() { + super.initialize(); + if (mHorizontalContentContainer != null) { + mHorizontalContentContainer.setClipChildren(false); + } + } + @Override public TileLayout getOrCreateTileLayout() { QQSSideLabelTileLayout layout = new QQSSideLabelTileLayout(mContext); @@ -109,10 +160,15 @@ public void setMaxTiles(int maxTiles) { @Override public void onTuningChanged(String key, String newValue) { - if (QS_SHOW_BRIGHTNESS.equals(key)) { - // No Brightness or Tooltip for you! - super.onTuningChanged(key, "0"); - } + switch (key) { + case QS_SHOW_BRIGHTNESS_SLIDER: + boolean value = + TunerService.parseInteger(newValue, 2) > 1; + super.onTuningChanged(key, value ? newValue : "0"); + break; + default: + super.onTuningChanged(key, newValue); + } } public int getNumQuickTiles() { diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java index 9739974256f6..e1878b8a84ca 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java @@ -33,6 +33,11 @@ import com.android.systemui.qs.dagger.QSScope; import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.util.leak.RotationUtils; +import com.android.systemui.settings.brightness.BrightnessController; +import com.android.systemui.settings.brightness.BrightnessMirrorHandler; +import com.android.systemui.settings.brightness.BrightnessSliderController; +import com.android.systemui.statusbar.policy.BrightnessMirrorController; +import com.android.systemui.tuner.TunerService; import java.util.ArrayList; import java.util.List; @@ -46,6 +51,11 @@ public class QuickQSPanelController extends QSPanelControllerBase { private final Provider mUsingCollapsedLandscapeMediaProvider; + private final BrightnessController mBrightnessController; + private final TunerService mTunerService; + private final BrightnessSliderController mBrightnessSliderController; + private final BrightnessMirrorHandler mBrightnessMirrorHandler; + private BrightnessMirrorController mBrightnessMirrorController; @Inject QuickQSPanelController(QuickQSPanel view, QSTileHost qsTileHost, @@ -55,11 +65,21 @@ public class QuickQSPanelController extends QSPanelControllerBase @Named(QS_USING_COLLAPSED_LANDSCAPE_MEDIA) Provider usingCollapsedLandscapeMediaProvider, MetricsLogger metricsLogger, UiEventLogger uiEventLogger, QSLogger qsLogger, - DumpManager dumpManager + DumpManager dumpManager, + TunerService tunerService, + BrightnessController.Factory brightnessControllerFactory, + BrightnessSliderController.Factory brightnessSliderFactory ) { super(view, qsTileHost, qsCustomizerController, usingMediaPlayer, mediaHost, metricsLogger, uiEventLogger, qsLogger, dumpManager); mUsingCollapsedLandscapeMediaProvider = usingCollapsedLandscapeMediaProvider; + mTunerService = tunerService; + + mBrightnessSliderController = brightnessSliderFactory.create(getContext(), mView); + mView.setBrightnessView(mBrightnessSliderController.getRootView()); + + mBrightnessController = brightnessControllerFactory.create(mBrightnessSliderController); + mBrightnessMirrorHandler = new BrightnessMirrorHandler(mBrightnessController); } @Override @@ -68,6 +88,7 @@ protected void onInit() { updateMediaExpansion(); mMediaHost.setShowsOnlyActiveMedia(true); mMediaHost.init(MediaHierarchyManager.LOCATION_QQS); + mBrightnessSliderController.init(); } private void updateMediaExpansion() { @@ -90,11 +111,48 @@ protected int getRotation() { @Override protected void onViewAttached() { super.onViewAttached(); + + mTunerService.addTunable(mView, QSPanel.QS_BRIGHTNESS_SLIDER_POSITION); + mTunerService.addTunable(mView, QSPanel.QS_SHOW_AUTO_BRIGHTNESS); + mTunerService.addTunable(mView, QSPanel.QS_SHOW_BRIGHTNESS_SLIDER); + + mView.setBrightnessRunnable(() -> { + mView.updateResources(); + updateBrightnessMirror(); + }); + + mBrightnessMirrorHandler.onQsPanelAttached(); } @Override protected void onViewDetached() { super.onViewDetached(); + mTunerService.removeTunable(mView); + mView.setBrightnessRunnable(null); + mBrightnessMirrorHandler.onQsPanelDettached(); + } + + private void updateBrightnessMirror() { + if (mBrightnessMirrorController != null) { + mBrightnessSliderController.setMirrorControllerAndMirror(mBrightnessMirrorController); + } + } + + @Override + void setListening(boolean listening) { + super.setListening(listening); + + // Set the listening as soon as the QS fragment starts listening regardless of the + //expansion, so it will update the current brightness before the slider is visible. + if (listening) { + mBrightnessController.registerCallbacks(); + } else { + mBrightnessController.unregisterCallbacks(); + } + } + + public boolean isListening() { + return mView.isListening(); } private void setMaxTiles(int parseNumTiles) { @@ -102,6 +160,12 @@ private void setMaxTiles(int parseNumTiles) { setTiles(); } + @Override + public void refreshAllTiles() { + mBrightnessController.checkRestrictionAndSetEnabled(); + super.refreshAllTiles(); + } + @Override protected void onConfigurationChanged() { int newMaxTiles = getResources().getInteger(R.integer.quick_qs_panel_max_tiles); @@ -130,4 +194,9 @@ public void setContentMargins(int marginStart, int marginEnd) { public int getNumQuickTiles() { return mView.getNumQuickTiles(); } + + public void setBrightnessMirror(BrightnessMirrorController brightnessMirrorController) { + mBrightnessMirrorController = brightnessMirrorController; + mBrightnessMirrorHandler.setController(brightnessMirrorController); + } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java index 264edb1ec9e4..567ed4191e3d 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java @@ -94,6 +94,10 @@ public class QuickStatusBarHeader extends FrameLayout { private StatusBarContentInsetsProvider mInsetsProvider; private int mRoundedCornerPadding = 0; + private int mStatusBarPaddingStart; + private int mStatusBarPaddingEnd; + private int mHeaderPaddingLeft; + private int mHeaderPaddingRight; private int mWaterfallTopInset; private int mCutOutPaddingLeft; private int mCutOutPaddingRight; @@ -105,6 +109,8 @@ public class QuickStatusBarHeader extends FrameLayout { private List mRssiIgnoredSlots = List.of(); private boolean mIsSingleCarrier; + private boolean mHasLeftCutout; + private boolean mHasRightCutout; private boolean mHasCenterCutout; private boolean mConfigShowBatteryEstimate; @@ -143,6 +149,7 @@ protected void onFinishInflate() { mClockContainer = findViewById(R.id.clock_container); mClockView = findViewById(R.id.clock); + mClockView.setQsHeader(); mDatePrivacySeparator = findViewById(R.id.space); // Tint for the battery icons are handled in setupHost() mBatteryRemainingIcon = findViewById(R.id.batteryRemainingIcon); @@ -182,6 +189,9 @@ void onAttach(TintedIconManager iconManager, void setIsSingleCarrier(boolean isSingleCarrier) { mIsSingleCarrier = isSingleCarrier; + if (mIsSingleCarrier) { + mIconContainer.removeIgnoredSlots(mRssiIgnoredSlots); + } updateAlphaAnimator(); } @@ -194,7 +204,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); if (mDatePrivacyView.getMeasuredHeight() != mTopViewMeasureHeight) { mTopViewMeasureHeight = mDatePrivacyView.getMeasuredHeight(); - post(this::updateAnimators); + updateAnimators(); } } @@ -245,6 +255,11 @@ void updateResources() { mRoundedCornerPadding = resources.getDimensionPixelSize( R.dimen.rounded_corner_content_padding); + mStatusBarPaddingStart = resources.getDimensionPixelSize( + R.dimen.status_bar_padding_start); + mStatusBarPaddingEnd = resources.getDimensionPixelSize( + R.dimen.status_bar_padding_end); + int qsOffsetHeight = SystemBarUtils.getQuickQsOffsetHeight(mContext); mDatePrivacyView.getLayoutParams().height = @@ -331,6 +346,12 @@ private void updateAlphaAnimator() { .addFloat(mDateView, "alpha", 0, 0, 1) .addFloat(mClockDateView, "alpha", 1, 0, 0) .addFloat(mQSCarriers, "alpha", 0, 1) + // Use statusbar paddings when collapsed, + // align with QS when expanded, and animate translation + .addFloat(isLayoutRtl() ? mRightLayout : mClockContainer, "translationX", + mHeaderPaddingLeft + mStatusBarPaddingStart, 0) + .addFloat(isLayoutRtl() ? mClockContainer: mRightLayout, "translationX", + -(mHeaderPaddingRight + mStatusBarPaddingEnd), 0) .setListener(new TouchAnimator.ListenerAdapter() { @Override public void onAnimationAtEnd() { @@ -383,6 +404,7 @@ public void setExpanded(boolean expanded, QuickQSPanelController quickQSPanelCon if (mExpanded == expanded) return; mExpanded = expanded; quickQSPanelController.setExpanded(expanded); + mDateView.setVisibility(mClockView.isClockDateEnabled() ? View.INVISIBLE : View.VISIBLE); updateEverything(); } @@ -436,8 +458,6 @@ public WindowInsets onApplyWindowInsets(WindowInsets insets) { .getStatusBarContentInsetsForCurrentRotation(); boolean hasCornerCutout = mInsetsProvider.currentRotationHasCornerCutout(); - mDatePrivacyView.setPadding(sbInsets.first, 0, sbInsets.second, 0); - mStatusIconsView.setPadding(sbInsets.first, 0, sbInsets.second, 0); LinearLayout.LayoutParams datePrivacySeparatorLayoutParams = (LinearLayout.LayoutParams) mDatePrivacySeparator.getLayoutParams(); LinearLayout.LayoutParams mClockIconsSeparatorLayoutParams = @@ -450,6 +470,12 @@ public WindowInsets onApplyWindowInsets(WindowInsets insets) { mClockIconsSeparatorLayoutParams.width = 0; setSeparatorVisibility(false); mShowClockIconsSeparator = false; + if (sbInsets.first != 0) { + mHasLeftCutout = true; + } + if (sbInsets.second != 0) { + mHasRightCutout = true; + } mHasCenterCutout = false; } else { datePrivacySeparatorLayoutParams.width = topCutout.width(); @@ -457,6 +483,8 @@ public WindowInsets onApplyWindowInsets(WindowInsets insets) { mClockIconsSeparatorLayoutParams.width = topCutout.width(); mShowClockIconsSeparator = true; setSeparatorVisibility(mKeyguardExpansionFraction == 0f); + mHasLeftCutout = false; + mHasRightCutout = false; mHasCenterCutout = true; } } @@ -501,34 +529,38 @@ private void setSeparatorVisibility(boolean visible) { private void updateHeadersPadding() { setContentMargins(mDatePrivacyView, 0, 0); setContentMargins(mStatusIconsView, 0, 0); - int paddingLeft = 0; - int paddingRight = 0; FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams(); + // Note: these are supposedly notification_side_paddings int leftMargin = lp.leftMargin; int rightMargin = lp.rightMargin; // The clock might collide with cutouts, let's shift it out of the way. - // We only do that if the inset is bigger than our own padding, since it's nicer to - // align with - if (mCutOutPaddingLeft > 0) { - // if there's a cutout, let's use at least the rounded corner inset - int cutoutPadding = Math.max(mCutOutPaddingLeft, mRoundedCornerPadding); - paddingLeft = Math.max(cutoutPadding - leftMargin, 0); + // Margin will be the reference point of paddings/translations + // and will have to be subtracted from cutout paddings + boolean headerPaddingUpdated = false; + int headerPaddingLeft = Math.max(mCutOutPaddingLeft, mRoundedCornerPadding) - leftMargin; + if (headerPaddingLeft != mHeaderPaddingLeft) { + mHeaderPaddingLeft = headerPaddingLeft; + headerPaddingUpdated = true; } - if (mCutOutPaddingRight > 0) { - // if there's a cutout, let's use at least the rounded corner inset - int cutoutPadding = Math.max(mCutOutPaddingRight, mRoundedCornerPadding); - paddingRight = Math.max(cutoutPadding - rightMargin, 0); + int headerPaddingRight = Math.max(mCutOutPaddingRight, mRoundedCornerPadding) - rightMargin; + if (headerPaddingRight != mHeaderPaddingRight) { + mHeaderPaddingRight = headerPaddingRight; + headerPaddingUpdated = true; } - mDatePrivacyView.setPadding(paddingLeft, + // Update header animator with new paddings + if (headerPaddingUpdated) { + updateAnimators(); + } + mDatePrivacyView.setPadding(mHeaderPaddingLeft + mStatusBarPaddingStart, mWaterfallTopInset, - paddingRight, + mHeaderPaddingRight + mStatusBarPaddingEnd, 0); - mStatusIconsView.setPadding(paddingLeft, + mStatusIconsView.setPadding(0, mWaterfallTopInset, - paddingRight, + 0, 0); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java index ccaab1adaf26..aa938ed0295c 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java @@ -18,10 +18,8 @@ import android.os.Bundle; -import com.android.internal.colorextraction.ColorExtractor; import com.android.systemui.R; import com.android.systemui.battery.BatteryMeterViewController; -import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.demomode.DemoMode; import com.android.systemui.demomode.DemoModeController; import com.android.systemui.flags.FeatureFlags; @@ -66,9 +64,6 @@ class QuickStatusBarHeaderController extends ViewController { - final boolean lightTheme = mColorExtractor.getNeutralColors().supportsDarkText(); - mClockView.onColorsChanged(lightTheme); - }; - mColorExtractor.addOnColorsChangedListener(mOnColorsChangedListener); // Don't need to worry about tuner settings for this icon mBatteryMeterViewController.ignoreTunerUpdates(); @@ -153,7 +141,6 @@ protected void onViewAttached() { @Override protected void onViewDetached() { - mColorExtractor.removeOnColorsChangedListener(mOnColorsChangedListener); mPrivacyIconsController.onParentInvisible(); mStatusBarIconController.removeIconGroup(mIconManager); mQSCarrierGroupController.setOnSingleCarrierChangedListener(null); diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java index cf10c7940871..d11c9523c4ee 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java +++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java @@ -20,6 +20,9 @@ import android.animation.AnimatorListenerAdapter; import android.content.Context; import android.content.res.Configuration; +import android.graphics.Color; +import android.text.SpannableString; +import android.text.style.ForegroundColorSpan; import android.util.AttributeSet; import android.util.TypedValue; import android.view.LayoutInflater; @@ -74,8 +77,11 @@ public QSCustomizer(Context context, AttributeSet attrs) { toolbar.setNavigationIcon( getResources().getDrawable(value.resourceId, mContext.getTheme())); - toolbar.getMenu().add(Menu.NONE, MENU_RESET, 0, + SpannableString resetText = new SpannableString( mContext.getString(com.android.internal.R.string.reset)); + resetText.setSpan(new ForegroundColorSpan(isNightMode() ? + Color.WHITE : Color.BLACK), 0, resetText.length(), 0); + toolbar.getMenu().add(Menu.NONE, MENU_RESET, 0, resetText); toolbar.setTitle(R.string.qs_edit); mRecyclerView = findViewById(android.R.id.list); mTransparentView = findViewById(R.id.customizer_transparent_view); @@ -84,6 +90,11 @@ public QSCustomizer(Context context, AttributeSet attrs) { mRecyclerView.setItemAnimator(animator); } + private boolean isNightMode() { + return (mContext.getResources().getConfiguration().uiMode + & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES; + } + void updateResources() { LayoutParams lp = (LayoutParams) mTransparentView.getLayoutParams(); lp.height = QSUtils.getQsHeaderSystemIconsAreaHeight(mContext); @@ -92,12 +103,8 @@ void updateResources() { } void updateNavBackDrop(Configuration newConfig, LightBarController lightBarController) { - View navBackdrop = findViewById(R.id.nav_bar_background); mIsShowingNavBackdrop = newConfig.smallestScreenWidthDp >= 600 || newConfig.orientation != Configuration.ORIENTATION_LANDSCAPE; - if (navBackdrop != null) { - navBackdrop.setVisibility(mIsShowingNavBackdrop ? View.VISIBLE : View.GONE); - } updateNavColors(lightBarController); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java index d84b12c714bd..77bc1e795477 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java +++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java @@ -23,9 +23,11 @@ import android.graphics.drawable.Drawable; import android.os.Handler; import android.view.LayoutInflater; +import android.view.MotionEvent; import android.view.View; import android.view.View.OnClickListener; import android.view.View.OnLayoutChangeListener; +import android.view.View.OnTouchListener; import android.view.ViewGroup; import android.widget.FrameLayout; import android.widget.TextView; @@ -331,7 +333,7 @@ public void onBindViewHolder(final Holder holder, int position) { final String titleText; Resources res = mContext.getResources(); if (mCurrentDrag == null) { - titleText = res.getString(R.string.drag_to_add_tiles); + titleText = res.getString(R.string.tap_to_add_tiles); } else if (!canRemoveTiles() && mCurrentDrag.getAdapterPosition() < mEditIndex) { titleText = res.getString(R.string.drag_to_remove_disabled, mMinNumTiles); } else { @@ -412,6 +414,22 @@ public void onClick(View v) { if (position == mFocusIndex) { focusOnHolder(holder); } + holder.mTileView.setOnTouchListener(new OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent ev) { + if (ev.getAction() == MotionEvent.ACTION_UP) { + int position = holder.getLayoutPosition(); + if (position < mEditIndex) { + if (canRemoveTiles()) { + move(position, mEditIndex, true); + } + } else { + move(position, mEditIndex, true); + } + } + return false; + } + }); } private void focusOnHolder(Holder holder) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java index a92c7e3c8554..25e68aa4b766 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java @@ -31,12 +31,16 @@ import com.android.systemui.qs.tiles.AlarmTile; import com.android.systemui.qs.tiles.BatterySaverTile; import com.android.systemui.qs.tiles.BluetoothTile; +import com.android.systemui.qs.tiles.CPUInfoTile; import com.android.systemui.qs.tiles.CameraToggleTile; +import com.android.systemui.qs.tiles.CaffeineTile; import com.android.systemui.qs.tiles.CastTile; import com.android.systemui.qs.tiles.CellularTile; import com.android.systemui.qs.tiles.ColorCorrectionTile; import com.android.systemui.qs.tiles.ColorInversionTile; +import com.android.systemui.qs.tiles.CompassTile; import com.android.systemui.qs.tiles.DataSaverTile; +import com.android.systemui.qs.tiles.DataSwitchTile; import com.android.systemui.qs.tiles.DeviceControlsTile; import com.android.systemui.qs.tiles.DndTile; import com.android.systemui.qs.tiles.DreamTile; @@ -53,7 +57,9 @@ import com.android.systemui.qs.tiles.ReduceBrightColorsTile; import com.android.systemui.qs.tiles.RotationLockTile; import com.android.systemui.qs.tiles.ScreenRecordTile; +import com.android.systemui.qs.tiles.SmartPixelsTile; import com.android.systemui.qs.tiles.UiModeNightTile; +import com.android.systemui.qs.tiles.WeatherTile; import com.android.systemui.qs.tiles.WifiTile; import com.android.systemui.qs.tiles.WorkModeTile; import com.android.systemui.util.leak.GarbageMonitor; @@ -98,6 +104,12 @@ public class QSFactoryImpl implements QSFactory { private final Provider mQRCodeScannerTileProvider; private final Provider mOneHandedModeTileProvider; private final Provider mDreamTileProvider; + private final Provider mCaffeineTileProvider; + private final Provider mSmartPixelsTileProvider; + private final Provider mCPUInfoTileProvider; + private final Provider mCompassTileProvider; + private final Provider mDataSwitchTileProvider; + private final Provider mWeatherTileProvider; private final Lazy mQsHostLazy; private final Provider mCustomTileBuilderProvider; @@ -135,7 +147,13 @@ public QSFactoryImpl( Provider qrCodeScannerTileProvider, Provider oneHandedModeTileProvider, Provider colorCorrectionTileProvider, - Provider dreamTileProvider) { + Provider dreamTileProvider, + Provider caffeineTileProvider, + Provider smartPixelsTileProvider, + Provider cpuInfoTileProvider, + Provider compassTileProvider, + Provider dataSwitchTileProvider, + Provider weatherTileProvider) { mQsHostLazy = qsHostLazy; mCustomTileBuilderProvider = customTileBuilderProvider; @@ -169,6 +187,12 @@ public QSFactoryImpl( mOneHandedModeTileProvider = oneHandedModeTileProvider; mColorCorrectionTileProvider = colorCorrectionTileProvider; mDreamTileProvider = dreamTileProvider; + mCaffeineTileProvider = caffeineTileProvider; + mSmartPixelsTileProvider = smartPixelsTileProvider; + mCPUInfoTileProvider = cpuInfoTileProvider; + mCompassTileProvider = compassTileProvider; + mDataSwitchTileProvider = dataSwitchTileProvider; + mWeatherTileProvider = weatherTileProvider; } /** Creates a tile with a type based on {@code tileSpec} */ @@ -244,6 +268,18 @@ protected QSTileImpl createTileInternal(String tileSpec) { return mColorCorrectionTileProvider.get(); case "dream": return mDreamTileProvider.get(); + case "caffeine": + return mCaffeineTileProvider.get(); + case "smartpixels": + return mSmartPixelsTileProvider.get(); + case "cpuinfo": + return mCPUInfoTileProvider.get(); + case "compass": + return mCompassTileProvider.get(); + case "dataswitch": + return mDataSwitchTileProvider.get(); + case "weather": + return mWeatherTileProvider.get(); } // Custom tiles diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java index e9a6c25c0e6d..203d837216c5 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java @@ -244,7 +244,7 @@ private static int getIconColorForState(Context context, QSTile.State state) { return Utils.getColorAttrDefaultColor(context, android.R.attr.textColorPrimary); } else if (state.state == Tile.STATE_ACTIVE) { return Utils.getColorAttrDefaultColor(context, - com.android.internal.R.attr.textColorOnAccent); + com.android.internal.R.attr.textColorPrimaryInverse); } else { Log.e("QSIconView", "Invalid state " + state); return 0; diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt index 972b24343d10..65e8e64bc938 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt @@ -92,12 +92,12 @@ open class QSTileViewImpl @JvmOverloads constructor( } private val colorActive = Utils.getColorAttrDefaultColor(context, - com.android.internal.R.attr.colorAccentPrimary) + android.R.attr.colorAccent) private val colorInactive = Utils.getColorAttrDefaultColor(context, R.attr.offStateColor) private val colorUnavailable = Utils.applyAlpha(UNAVAILABLE_ALPHA, colorInactive) private val colorLabelActive = - Utils.getColorAttrDefaultColor(context, com.android.internal.R.attr.textColorOnAccent) + Utils.getColorAttrDefaultColor(context, com.android.internal.R.attr.textColorPrimaryInverse) private val colorLabelInactive = Utils.getColorAttrDefaultColor(context, android.R.attr.textColorPrimary) private val colorLabelUnavailable = @@ -483,7 +483,7 @@ open class QSTileViewImpl @JvmOverloads constructor( } if (!Objects.equals(secondaryLabel.text, state.secondaryLabel)) { secondaryLabel.text = state.secondaryLabel - secondaryLabel.visibility = if (TextUtils.isEmpty(state.secondaryLabel)) { + secondaryLabel.visibility = if (TextUtils.isEmpty(secondaryLabel.text)) { GONE } else { VISIBLE diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CPUInfoTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CPUInfoTile.java new file mode 100644 index 000000000000..894a2e02e66f --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CPUInfoTile.java @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2017-2018 Benzo Rom + * (C) 2017-2021 crDroidAndroid Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles; + +import android.content.Intent; +import android.os.Handler; +import android.os.Looper; +import android.provider.Settings; +import android.provider.Settings.Secure; +import android.service.quicksettings.Tile; +import android.view.View; + +import androidx.annotation.Nullable; + +import com.android.systemui.R; +import com.android.systemui.dagger.qualifiers.Background; +import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.plugins.FalsingManager; +import com.android.systemui.plugins.qs.QSTile.BooleanState; +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.qs.SettingObserver; +import com.android.systemui.qs.QSHost; +import com.android.systemui.qs.logging.QSLogger; +import com.android.systemui.qs.tileimpl.QSTileImpl; +import com.android.systemui.util.settings.SecureSettings; + +import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.nano.MetricsProto.MetricsEvent; + +import javax.inject.Inject; + +/** Quick settings tile: CPUInfo overlay **/ +public class CPUInfoTile extends QSTileImpl { + + private final SettingObserver mSetting; + private final Icon mIcon = ResourceIcon.get(R.drawable.ic_qs_cpu_info); + + @Inject + public CPUInfoTile( + QSHost host, + @Background Looper backgroundLooper, + @Main Handler mainHandler, + FalsingManager falsingManager, + MetricsLogger metricsLogger, + StatusBarStateController statusBarStateController, + ActivityStarter activityStarter, + QSLogger qsLogger, + SecureSettings secureSettings) { + super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger, + statusBarStateController, activityStarter, qsLogger); + + mSetting = new SettingObserver(secureSettings, mHandler, Secure.SHOW_CPU_OVERLAY) { + @Override + protected void handleValueChanged(int value, boolean observedChange) { + handleRefreshState(value); + } + }; + } + + @Override + public BooleanState newTileState() { + BooleanState state = new BooleanState(); + state.handlesLongClick = false; + return state; + } + + @Override + protected void handleClick(@Nullable View view) { + mSetting.setValue(mState.value ? 0 : 1); + refreshState(); + toggleState(); + } + + protected void toggleState() { + Intent service = (new Intent()) + .setClassName("com.android.systemui", + "com.android.systemui.CPUInfoService"); + if (mSetting.getValue() == 0) { + mContext.stopService(service); + } else { + mContext.startService(service); + } + } + + @Override + public Intent getLongClickIntent() { + return null; + } + + @Override + protected void handleUpdateState(BooleanState state, Object arg) { + if (mSetting == null) return; + final int value = arg instanceof Integer ? (Integer)arg : mSetting.getValue(); + final boolean cpuInfoEnabled = value != 0; + state.value = cpuInfoEnabled; + state.label = mContext.getString(R.string.quick_settings_cpuinfo_label); + state.icon = mIcon; + state.contentDescription = mContext.getString( + R.string.quick_settings_cpuinfo_label); + if (cpuInfoEnabled) { + state.state = Tile.STATE_ACTIVE; + } else { + state.state = Tile.STATE_INACTIVE; + } + } + + @Override + public CharSequence getTileLabel() { + return mContext.getString(R.string.quick_settings_cpuinfo_label); + } + + @Override + public int getMetricsCategory() { + return MetricsEvent.BOOTLEG; + } + + @Override + public void handleSetListening(boolean listening) { + // Do nothing + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CaffeineTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CaffeineTile.java new file mode 100644 index 000000000000..01f784bab66b --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CaffeineTile.java @@ -0,0 +1,251 @@ +/* + * Copyright (C) 2016 The CyanogenMod Project + * Copyright (c) 2017 The LineageOS Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.CountDownTimer; +import android.os.Handler; +import android.os.Looper; +import android.os.PowerManager; +import android.os.SystemClock; +import android.service.quicksettings.Tile; +import android.view.View; + +import androidx.annotation.Nullable; + +import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.nano.MetricsProto.MetricsEvent; +import com.android.systemui.dagger.qualifiers.Background; +import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.qs.QSHost; +import com.android.systemui.qs.logging.QSLogger; +import com.android.systemui.qs.tileimpl.QSTileImpl; +import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.plugins.FalsingManager; +import com.android.systemui.plugins.qs.QSTile.BooleanState; +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.R; + +import javax.inject.Inject; + +/** Quick settings tile: Caffeine **/ +public class CaffeineTile extends QSTileImpl { + + private final Icon mIcon = ResourceIcon.get(R.drawable.ic_qs_caffeine); + + private final PowerManager.WakeLock mWakeLock; + private int mSecondsRemaining; + private int mDuration; + private static int[] DURATIONS = new int[] { + 5 * 60, // 5 min + 10 * 60, // 10 min + 30 * 60, // 30 min + -1, // infinity + }; + private CountDownTimer mCountdownTimer = null; + public long mLastClickTime = -1; + private final Receiver mReceiver = new Receiver(); + + @Inject + public CaffeineTile( + QSHost host, + @Background Looper backgroundLooper, + @Main Handler mainHandler, + FalsingManager falsingManager, + MetricsLogger metricsLogger, + StatusBarStateController statusBarStateController, + ActivityStarter activityStarter, + QSLogger qsLogger + ) { + super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger, + statusBarStateController, activityStarter, qsLogger); + mWakeLock = mContext.getSystemService(PowerManager.class).newWakeLock( + PowerManager.FULL_WAKE_LOCK, "CaffeineTile"); + mReceiver.init(); + } + + @Override + public BooleanState newTileState() { + return new BooleanState(); + } + + @Override + protected void handleDestroy() { + super.handleDestroy(); + stopCountDown(); + mReceiver.destroy(); + if (mWakeLock.isHeld()) { + mWakeLock.release(); + } + } + + @Override + public void handleClick(@Nullable View view) { + // If last user clicks < 5 seconds + // we cycle different duration + // otherwise toggle on/off + if (mWakeLock.isHeld() && (mLastClickTime != -1) && + (SystemClock.elapsedRealtime() - mLastClickTime < 5000)) { + // cycle duration + mDuration++; + if (mDuration >= DURATIONS.length) { + // all durations cycled, turn if off + mDuration = -1; + stopCountDown(); + if (mWakeLock.isHeld()) { + mWakeLock.release(); + } + } else { + // change duration + startCountDown(DURATIONS[mDuration]); + if (!mWakeLock.isHeld()) { + mWakeLock.acquire(); + } + } + } else { + // toggle + if (mWakeLock.isHeld()) { + mWakeLock.release(); + stopCountDown(); + } else { + mWakeLock.acquire(); + mDuration = 0; + startCountDown(DURATIONS[mDuration]); + } + } + mLastClickTime = SystemClock.elapsedRealtime(); + refreshState(); + } + + @Override + protected void handleLongClick(@Nullable View view) { + // Set duration to infinity on long click + int infinityIndex = DURATIONS.length - 1; + if (mLastClickTime == infinityIndex) { + // Already at infinity + return; + } + mDuration = infinityIndex; + startCountDown(DURATIONS[mDuration]); + if (!mWakeLock.isHeld()) { + mWakeLock.acquire(); + } + mLastClickTime = SystemClock.elapsedRealtime(); + refreshState(); + } + + @Override + public Intent getLongClickIntent() { + return null; + } + + @Override + public CharSequence getTileLabel() { + return mContext.getString(R.string.quick_settings_caffeine_label); + } + + @Override + public int getMetricsCategory() { + return MetricsEvent.BOOTLEG; + } + + private void startCountDown(long duration) { + stopCountDown(); + mSecondsRemaining = (int)duration; + if (duration == -1) { + // infinity timing, no need to start timer + return; + } + mCountdownTimer = new CountDownTimer(duration * 1000, 1000) { + @Override + public void onTick(long millisUntilFinished) { + mSecondsRemaining = (int) (millisUntilFinished / 1000); + refreshState(); + } + + @Override + public void onFinish() { + if (mWakeLock.isHeld()) + mWakeLock.release(); + refreshState(); + } + + }.start(); + } + + private void stopCountDown() { + if (mCountdownTimer != null) { + mCountdownTimer.cancel(); + mCountdownTimer = null; + } + } + + private String formatValueWithRemainingTime() { + if (mSecondsRemaining == -1) { + return "\u221E"; // infinity + } + return String.format("%02d:%02d", + mSecondsRemaining / 60 % 60, mSecondsRemaining % 60); + } + + @Override + protected void handleUpdateState(BooleanState state, Object arg) { + state.value = mWakeLock.isHeld(); + state.icon = mIcon; + state.label = mContext.getString(R.string.quick_settings_caffeine_label); + if (state.value) { + state.secondaryLabel = formatValueWithRemainingTime(); + state.contentDescription = mContext.getString( + R.string.accessibility_quick_settings_caffeine_on); + state.state = Tile.STATE_ACTIVE; + } else { + state.secondaryLabel = null; + state.contentDescription = mContext.getString( + R.string.accessibility_quick_settings_caffeine_off); + state.state = Tile.STATE_INACTIVE; + } + } + + private final class Receiver extends BroadcastReceiver { + public void init() { + // Register for Intent broadcasts for... + IntentFilter filter = new IntentFilter(); + filter.addAction(Intent.ACTION_SCREEN_OFF); + mContext.registerReceiver(this, filter, null, mHandler); + } + + public void destroy() { + mContext.unregisterReceiver(this); + } + + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + if (Intent.ACTION_SCREEN_OFF.equals(action)) { + // disable caffeine if user force off (power button) + stopCountDown(); + if (mWakeLock.isHeld()) + mWakeLock.release(); + refreshState(); + } + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java index a31500c6bb18..6cc21adea3da 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java @@ -129,6 +129,12 @@ protected void handleUpdateState(BooleanState state, Object arg) { state.contentDescription = state.label; } + @Override + public boolean isAvailable() { + return mContext.getResources().getBoolean( + com.android.internal.R.bool.config_displayInversionAvailable); + } + @Override public int getMetricsCategory() { return MetricsEvent.QS_COLORINVERSION; diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CompassTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CompassTile.java new file mode 100644 index 000000000000..4ac970b266ad --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CompassTile.java @@ -0,0 +1,257 @@ +/* + * Copyright (C) 2019-2022 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.systemui.qs.tiles; + +import android.content.Context; +import android.content.Intent; +import android.hardware.Sensor; +import android.hardware.SensorEvent; +import android.hardware.SensorEventListener; +import android.hardware.SensorManager; +import android.service.quicksettings.Tile; +import android.widget.ImageView; + +import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.nano.MetricsProto.MetricsEvent; +import com.android.systemui.R; +import com.android.systemui.plugins.qs.QSIconView; +import com.android.systemui.plugins.qs.QSTile.BooleanState; +import com.android.systemui.qs.QSHost; +import com.android.systemui.qs.tileimpl.QSTileImpl; +import android.service.quicksettings.Tile; + +import android.os.Handler; +import android.os.Looper; +import android.view.View; + +import androidx.annotation.Nullable; + +import com.android.internal.logging.MetricsLogger; +import com.android.systemui.dagger.qualifiers.Background; +import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.plugins.FalsingManager; +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.qs.logging.QSLogger; + +import javax.inject.Inject; + +public class CompassTile extends QSTileImpl implements SensorEventListener { + private final static float ALPHA = 0.97f; + + private boolean mActive = false; + + private SensorManager mSensorManager; + private Sensor mAccelerationSensor; + private Sensor mGeomagneticFieldSensor; + + private float[] mAcceleration; + private float[] mGeomagnetic; + + private ImageView mImage; + private boolean mListeningSensors; + private Handler mHandler; + + @Inject + public CompassTile(QSHost host, + @Background Looper backgroundLooper, + @Main Handler mainHandler, + FalsingManager falsingManager, + MetricsLogger metricsLogger, + StatusBarStateController statusBarStateController, + ActivityStarter activityStarter, + QSLogger qsLogger + ) { + super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger, + statusBarStateController, activityStarter, qsLogger); + mHandler = mainHandler; + mSensorManager = (SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE); + mAccelerationSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); + mGeomagneticFieldSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD); + } + + @Override + public BooleanState newTileState() { + BooleanState state = new BooleanState(); + state.handlesLongClick = false; + return state; + } + + @Override + protected void handleDestroy() { + super.handleDestroy(); + setListeningSensors(false); + mSensorManager = null; + mImage = null; + } + + @Override + public QSIconView createTileView(Context context) { + QSIconView iconView = super.createTileView(context); + mImage = (ImageView) iconView.findViewById(android.R.id.icon); + return iconView; + } + + @Override + protected void handleClick(@Nullable View view) { + mActive = !mActive; + refreshState(); + setListeningSensors(mActive); + } + + @Override + public void handleLongClick(@Nullable View view) { + handleClick(view); + } + + @Override + public Intent getLongClickIntent() { + return null; + } + + private void setListeningSensors(boolean listening) { + if (listening == mListeningSensors) return; + mListeningSensors = listening; + if (mListeningSensors) { + mSensorManager.registerListener( + this, mAccelerationSensor, SensorManager.SENSOR_DELAY_GAME); + mSensorManager.registerListener( + this, mGeomagneticFieldSensor, SensorManager.SENSOR_DELAY_GAME); + } else { + mSensorManager.unregisterListener(this); + } + } + + @Override + public CharSequence getTileLabel() { + return mContext.getString(R.string.quick_settings_compass_label); + } + + @Override + protected void handleUpdateState(BooleanState state, Object arg) { + final Float degrees = arg == null ? 0 : (Float) arg; + + state.value = mActive; + state.icon = ResourceIcon.get(R.drawable.ic_qs_compass); + + if (state.value) { + state.state = Tile.STATE_ACTIVE; + if (arg != null) { + state.label = formatValueWithCardinalDirection(degrees); + mHandler.post(() -> { + if (mImage == null) + return; + float target = 360 - degrees; + float relative = target - mImage.getRotation(); + if (relative > 180) relative -= 360; + mImage.setRotation(mImage.getRotation() + relative / 2); + }); + } else { + state.label = mContext.getString(R.string.quick_settings_compass_init); + mHandler.post(() -> { + if (mImage != null) + mImage.setRotation(0); + }); + } + } else { + state.label = mContext.getString(R.string.quick_settings_compass_label); + state.state = Tile.STATE_INACTIVE; + mHandler.post(() -> { + if (mImage != null) + mImage.setRotation(0); + }); + } + } + + @Override + public int getMetricsCategory() { + return MetricsEvent.BOOTLEG; + } + + @Override + public boolean isAvailable() { + return mSensorManager != null && mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER) != null + && mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD) != null; + } + + @Override + public void handleSetListening(boolean listening) { + if (!listening) { + setListeningSensors(false); + mActive = false; + } + } + + private String formatValueWithCardinalDirection(float degree) { + int cardinalDirectionIndex = (int) (Math.floor(((degree - 22.5) % 360) / 45) + 1) % 8; + String[] cardinalDirections = mContext.getResources().getStringArray( + R.array.cardinal_directions); + + return mContext.getString(R.string.quick_settings_compass_value, degree, + cardinalDirections[cardinalDirectionIndex]); + } + + @Override + public void onSensorChanged(SensorEvent event) { + float[] values; + if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) { + if (mAcceleration == null) { + mAcceleration = event.values.clone(); + } + + values = mAcceleration; + } else { + // Magnetic field sensor + if (mGeomagnetic == null) { + mGeomagnetic = event.values.clone(); + } + + values = mGeomagnetic; + } + + for (int i = 0; i < 3; i++) { + values[i] = ALPHA * values[i] + (1 - ALPHA) * event.values[i]; + } + + if (!mActive || !mListeningSensors || mAcceleration == null || mGeomagnetic == null) { + // Nothing to do at this moment + return; + } + + float R[] = new float[9]; + float I[] = new float[9]; + if (!SensorManager.getRotationMatrix(R, I, mAcceleration, mGeomagnetic)) { + // Rotation matrix couldn't be calculated + return; + } + + // Get the current orientation + float[] orientation = new float[3]; + SensorManager.getOrientation(R, orientation); + + // Convert azimuth to degrees + Float newDegree = Float.valueOf((float) Math.toDegrees(orientation[0])); + newDegree = (newDegree + 360) % 360; + + refreshState(newDegree); + } + + @Override + public void onAccuracyChanged(Sensor sensor, int accuracy) { + // noop + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSwitchTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSwitchTile.java new file mode 100644 index 000000000000..d037ec78d6a7 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSwitchTile.java @@ -0,0 +1,273 @@ +/* + * Copyright (C) 2020-2022 crDroidAndroid Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.qs.tiles; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.AsyncTask; +import android.os.Handler; +import android.os.Looper; +import android.os.SystemProperties; +import android.provider.Settings; +import android.telephony.PhoneStateListener; +import android.telephony.SubscriptionInfo; +import android.telephony.SubscriptionManager; +import android.telephony.TelephonyManager; +import android.text.TextUtils; +import android.util.Log; +import android.view.View; + +import androidx.annotation.Nullable; + +import com.android.internal.logging.MetricsLogger; + +import com.android.internal.logging.nano.MetricsProto.MetricsEvent; +import com.android.internal.telephony.IccCardConstants; +import com.android.internal.telephony.TelephonyIntents; +import com.android.systemui.R; +import com.android.systemui.dagger.qualifiers.Background; +import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.plugins.FalsingManager; +import com.android.systemui.plugins.qs.QSTile.BooleanState; +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.qs.QSHost; +import com.android.systemui.qs.logging.QSLogger; +import com.android.systemui.qs.tileimpl.QSTileImpl; +import com.android.systemui.qs.tileimpl.QSTileImpl.ResourceIcon; + +import java.util.List; + +import javax.inject.Inject; + +public class DataSwitchTile extends QSTileImpl { + private boolean mCanSwitch = true; + private boolean mRegistered = false; + private int mSimCount = 0; + BroadcastReceiver mSimReceiver = new BroadcastReceiver() { + public void onReceive(Context context, Intent intent) { + Log.d(TAG, "mSimReceiver:onReceive"); + refreshState(); + } + }; + private final MyCallStateListener mPhoneStateListener; + private final SubscriptionManager mSubscriptionManager; + private final TelephonyManager mTelephonyManager; + + class MyCallStateListener extends PhoneStateListener { + MyCallStateListener() { + } + + public void onCallStateChanged(int state, String arg1) { + mCanSwitch = mTelephonyManager.getCallState() == 0; + refreshState(); + } + } + + @Inject + public DataSwitchTile( + QSHost host, + @Background Looper backgroundLooper, + @Main Handler mainHandler, + FalsingManager falsingManager, + MetricsLogger metricsLogger, + StatusBarStateController statusBarStateController, + ActivityStarter activityStarter, + QSLogger qsLogger + ) { + super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger, + statusBarStateController, activityStarter, qsLogger); + mSubscriptionManager = SubscriptionManager.from(host.getContext()); + mTelephonyManager = TelephonyManager.from(host.getContext()); + mPhoneStateListener = new MyCallStateListener(); + } + + @Override + public boolean isAvailable() { + int count = TelephonyManager.getDefault().getPhoneCount(); + Log.d(TAG, "phoneCount: " + count); + return count >= 2; + } + + @Override + public BooleanState newTileState() { + return new BooleanState(); + } + + @Override + public void handleSetListening(boolean listening) { + if (listening) { + if (!mRegistered) { + IntentFilter filter = new IntentFilter(); + filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED); + mContext.registerReceiver(mSimReceiver, filter); + mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE); + mRegistered = true; + } + refreshState(); + } else if (mRegistered) { + mContext.unregisterReceiver(mSimReceiver); + mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE); + mRegistered = false; + } + } + + private void updateSimCount() { + String simState = SystemProperties.get("gsm.sim.state"); + Log.d(TAG, "DataSwitchTile:updateSimCount:simState=" + simState); + mSimCount = 0; + try { + String[] sims = TextUtils.split(simState, ","); + for (String sim : sims) { + if (!sim.isEmpty() + && !sim.equalsIgnoreCase(IccCardConstants.INTENT_VALUE_ICC_ABSENT) + && !sim.equalsIgnoreCase(IccCardConstants.INTENT_VALUE_ICC_NOT_READY)) { + mSimCount++; + } + } + } catch (Exception e) { + Log.e(TAG, "Error to parse sim state"); + } + Log.d(TAG, "DataSwitchTile:updateSimCount:mSimCount=" + mSimCount); + } + + @Override + protected void handleClick(@Nullable View view) { + if (!mCanSwitch) { + Log.d(TAG, "Call state=" + mTelephonyManager.getCallState()); + } else if (mSimCount == 0) { + Log.d(TAG, "handleClick:no sim card"); + } else if (mSimCount == 1) { + Log.d(TAG, "handleClick:only one sim card"); + } else { + AsyncTask.execute(() -> { + toggleMobileDataEnabled(); + refreshState(); + }); + mHost.collapsePanels(); + } + } + + @Override + public Intent getLongClickIntent() { + return new Intent(Settings.ACTION_NETWORK_OPERATOR_SETTINGS); + } + + @Override + public CharSequence getTileLabel() { + return mContext.getString(R.string.qs_data_switch_label); + } + + @Override + protected void handleUpdateState(BooleanState state, Object arg) { + boolean activeSIMZero; + if (arg == null) { + int defaultPhoneId = mSubscriptionManager.getDefaultDataPhoneId(); + Log.d(TAG, "default data phone id=" + defaultPhoneId); + activeSIMZero = defaultPhoneId == 0; + } else { + activeSIMZero = (Boolean) arg; + } + updateSimCount(); + switch (mSimCount) { + case 0: + state.icon = ResourceIcon.get(R.drawable.ic_qs_data_switch_0); + state.value = false; + state.secondaryLabel = mContext.getString(R.string.tile_unavailable); + break; + case 1: + state.icon = ResourceIcon.get(activeSIMZero + ? R.drawable.ic_qs_data_switch_2 + : R.drawable.ic_qs_data_switch_1); + state.value = false; + state.secondaryLabel = mContext.getString(R.string.tile_unavailable); + break; + case 2: + state.icon = ResourceIcon.get(activeSIMZero + ? R.drawable.ic_qs_data_switch_2 + : R.drawable.ic_qs_data_switch_1); + state.value = true; + state.secondaryLabel = getInactiveSlotName(); + break; + default: + state.icon = ResourceIcon.get(R.drawable.ic_qs_data_switch_1); + state.value = false; + state.secondaryLabel = mContext.getString(R.string.tile_unavailable); + break; + } + if (mSimCount < 2) { + state.state = 0; + } else if (!mCanSwitch) { + state.state = 0; + Log.d(TAG, "call state isn't idle, set to unavailable."); + } else { + state.state = state.value ? 2 : 1; + } + + state.label = mContext.getString(R.string.qs_data_switch_label); + } + + @Override + public int getMetricsCategory() { + return MetricsEvent.BOOTLEG; + } + + /** + * Set whether to enable data for {@code subId}, also whether to disable data for other + * subscription + */ + private void toggleMobileDataEnabled() { + TelephonyManager telephonyManager; + boolean dataEnabled = false; + boolean foundActive = false; + List subInfoList = + mSubscriptionManager.getActiveSubscriptionInfoList(true); + if (subInfoList != null) { + for (SubscriptionInfo subInfo : subInfoList) { + telephonyManager = + mTelephonyManager.createForSubscriptionId(subInfo.getSubscriptionId()); + dataEnabled = telephonyManager.getDataEnabled(); + if (!subInfo.isOpportunistic() || !dataEnabled) { + telephonyManager.setDataEnabled(!dataEnabled && !foundActive); + // Indicate we found sim with active data, disable data on remaining sim. + if (!foundActive) foundActive = !dataEnabled; + } + Log.d(TAG, "Changed subID " + subInfo.getSubscriptionId() + " to " + + !dataEnabled); + } + } + } + + private String getInactiveSlotName() { + TelephonyManager telephonyManager; + String mInitialState = mContext.getString(R.string.tile_unavailable); + List subInfoList = + mSubscriptionManager.getActiveSubscriptionInfoList(true); + if (subInfoList != null) { + for (SubscriptionInfo subInfo : subInfoList) { + telephonyManager = + mTelephonyManager.createForSubscriptionId(subInfo.getSubscriptionId()); + if (!telephonyManager.getDataEnabled()) { + // Inactive SIM found + return subInfo.getDisplayName().toString(); + } + } + } + return mInitialState; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/SmartPixelsTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/SmartPixelsTile.java new file mode 100644 index 000000000000..db865715c7e7 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/SmartPixelsTile.java @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2018 CarbonROM + * Copyright (C) 2018 Adin Kwok (adinkwok) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.os.Handler; +import android.os.Looper; +import android.os.PowerManager; +import android.os.UserHandle; +import android.provider.Settings; +import android.service.quicksettings.Tile; +import android.view.View; + +import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.nano.MetricsProto.MetricsEvent; +import com.android.systemui.dagger.qualifiers.Background; +import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.plugins.FalsingManager; +import com.android.systemui.plugins.qs.QSTile.BooleanState; +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.qs.QSHost; +import com.android.systemui.qs.logging.QSLogger; +import com.android.systemui.qs.tileimpl.QSTileImpl; +import com.android.systemui.R; +import com.android.systemui.statusbar.policy.BatteryController; + +import javax.inject.Inject; + +public class SmartPixelsTile extends QSTileImpl implements + BatteryController.BatteryStateChangeCallback { + + private static final Intent SMART_PIXELS_SETTINGS = new Intent("android.settings.SMART_PIXELS_SETTINGS"); + + private final BatteryController mBatteryController; + + private boolean mSmartPixelsEnable; + private boolean mSmartPixelsOnPowerSave; + private boolean mLowPowerMode; + private boolean mListening; + + @Inject + public SmartPixelsTile(QSHost host, + @Background Looper backgroundLooper, + @Main Handler mainHandler, + FalsingManager falsingManager, + MetricsLogger metricsLogger, + StatusBarStateController statusBarStateController, + ActivityStarter activityStarter, + QSLogger qsLogger, + BatteryController batteryController) { + super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger, + statusBarStateController, activityStarter, qsLogger); + mBatteryController = batteryController; + } + + @Override + public BooleanState newTileState() { + return new BooleanState(); + } + + @Override + public void handleSetListening(boolean listening) { + if (listening) { + mBatteryController.addCallback(this); + } else { + mBatteryController.removeCallback(this); + } + } + + @Override + public boolean isAvailable() { + return mContext.getResources(). + getBoolean(com.android.internal.R.bool.config_supportSmartPixels); + } + + @Override + public void handleClick(View v) { + mSmartPixelsEnable = (Settings.System.getIntForUser( + mContext.getContentResolver(), Settings.System.SMART_PIXELS_ENABLE, + 0, UserHandle.USER_CURRENT) == 1); + mSmartPixelsOnPowerSave = (Settings.System.getIntForUser( + mContext.getContentResolver(), Settings.System.SMART_PIXELS_ON_POWER_SAVE, + 0, UserHandle.USER_CURRENT) == 1); + if (mLowPowerMode && mSmartPixelsOnPowerSave) { + Settings.System.putIntForUser(mContext.getContentResolver(), + Settings.System.SMART_PIXELS_ON_POWER_SAVE, + 0, UserHandle.USER_CURRENT); + Settings.System.putIntForUser(mContext.getContentResolver(), + Settings.System.SMART_PIXELS_ENABLE, + 0, UserHandle.USER_CURRENT); + } else if (!mSmartPixelsEnable) { + Settings.System.putIntForUser(mContext.getContentResolver(), + Settings.System.SMART_PIXELS_ENABLE, + 1, UserHandle.USER_CURRENT); + } else { + Settings.System.putIntForUser(mContext.getContentResolver(), + Settings.System.SMART_PIXELS_ENABLE, + 0, UserHandle.USER_CURRENT); + } + refreshState(); + } + + @Override + public Intent getLongClickIntent() { + return SMART_PIXELS_SETTINGS; + } + + @Override + protected void handleUpdateState(BooleanState state, Object arg) { + mSmartPixelsEnable = (Settings.System.getIntForUser( + mContext.getContentResolver(), Settings.System.SMART_PIXELS_ENABLE, + 0, UserHandle.USER_CURRENT) == 1); + mSmartPixelsOnPowerSave = (Settings.System.getIntForUser( + mContext.getContentResolver(), Settings.System.SMART_PIXELS_ON_POWER_SAVE, + 0, UserHandle.USER_CURRENT) == 1); + state.icon = ResourceIcon.get(R.drawable.ic_qs_smart_pixels); + if (state.slash == null) { + state.slash = new SlashState(); + } + if (mLowPowerMode && mSmartPixelsOnPowerSave) { + state.label = mContext.getString(R.string.quick_settings_smart_pixels_on_power_save); + state.value = true; + } else if (mSmartPixelsEnable) { + state.label = mContext.getString(R.string.quick_settings_smart_pixels); + state.value = true; + } else { + state.label = mContext.getString(R.string.quick_settings_smart_pixels); + state.value = false; + } + state.slash.isSlashed = !state.value; + state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE; + } + + @Override + public CharSequence getTileLabel() { + return mContext.getString(R.string.quick_settings_smart_pixels); + } + + @Override + public int getMetricsCategory() { + return MetricsEvent.BOOTLEG; + } + + @Override + public void onBatteryLevelChanged(int level, boolean plugged, boolean charging) { + // yurt + } + + @Override + public void onPowerSaveChanged(boolean active) { + mLowPowerMode = active; + refreshState(); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WeatherTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WeatherTile.java new file mode 100644 index 000000000000..1aad819bf8cd --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WeatherTile.java @@ -0,0 +1,255 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * Copyright (C) 2017 The OmniROM project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles; + +import static com.android.internal.logging.MetricsLogger.VIEW_UNKNOWN; + +import android.content.Context; +import android.content.ComponentName; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.PaintFlagsDrawFilter; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.VectorDrawable; +import android.net.Uri; +import android.os.Handler; +import android.os.Looper; +import android.provider.Settings; +import android.service.quicksettings.Tile; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.ListView; +import android.widget.Toast; + +import androidx.annotation.Nullable; + +import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.nano.MetricsProto.MetricsEvent; +import com.android.internal.util.omni.OmniJawsClient; +import com.android.internal.util.bootleg.BootlegUtils; +import com.android.systemui.Dependency; +import com.android.systemui.R; +import com.android.systemui.dagger.qualifiers.Background; +import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.plugins.FalsingManager; +import com.android.systemui.plugins.qs.QSIconView; +import com.android.systemui.plugins.qs.QSTile.BooleanState; +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.qs.QSHost; +import com.android.systemui.qs.logging.QSLogger; +import com.android.systemui.qs.tileimpl.QSTileImpl; + +import javax.inject.Inject; + +import java.util.ArrayList; +import java.util.Arrays; + +public class WeatherTile extends QSTileImpl implements OmniJawsClient.OmniJawsObserver { + private static final String TAG = "WeatherTile"; + private static final boolean DEBUG = false; + private OmniJawsClient mWeatherClient; + private Drawable mWeatherImage; + private String mWeatherLabel; + private OmniJawsClient.WeatherInfo mWeatherData; + private boolean mEnabled; + private final ActivityStarter mActivityStarter; + private final Icon mIcon = ResourceIcon.get(R.drawable.ic_qs_weather_default_on); + + private static final String[] ALTERNATIVE_WEATHER_APPS = { + "cz.martykan.forecastie", + "com.accuweather.android", + "com.wunderground.android.weather", + "de.dwd.warnapp", + "mobi.lockdown.weather", + "uk.co.openweather", + "com.lifeoverflow.app.weather", + "ru.yandex.weatherplugin", + }; + + @Inject + public WeatherTile( + QSHost host, + @Background Looper backgroundLooper, + @Main Handler mainHandler, + FalsingManager falsingManager, + MetricsLogger metricsLogger, + StatusBarStateController statusBarStateController, + ActivityStarter activityStarter, + QSLogger qsLogger + ) { + super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger, + statusBarStateController, activityStarter, qsLogger); + mWeatherClient = new OmniJawsClient(mContext); + mEnabled = mWeatherClient.isOmniJawsEnabled(); + mActivityStarter = Dependency.get(ActivityStarter.class); + } + + @Override + public int getMetricsCategory() { + return MetricsEvent.BOOTLEG; + } + + @Override + public BooleanState newTileState() { + return new BooleanState(); + } + + @Override + public void handleSetListening(boolean listening) { + if (mWeatherClient == null) { + return; + } + if (DEBUG) Log.d(TAG, "setListening " + listening); + mEnabled = mWeatherClient.isOmniJawsEnabled(); + + if (listening) { + mWeatherClient.addObserver(this); + queryAndUpdateWeather(); + } else { + mWeatherClient.removeObserver(this); + } + } + + @Override + public void weatherUpdated() { + if (DEBUG) Log.d(TAG, "weatherUpdated"); + queryAndUpdateWeather(); + } + + @Override + public void weatherError(int errorReason) { + if (DEBUG) Log.d(TAG, "weatherError " + errorReason); + if (errorReason != OmniJawsClient.EXTRA_ERROR_DISABLED) { + mWeatherLabel = mContext.getResources().getString(R.string.omnijaws_service_error); + refreshState(); + } + } + + @Override + protected void handleDestroy() { + // make sure we dont left one + mWeatherClient.removeObserver(this); + mWeatherClient.cleanupObserver(); + super.handleDestroy(); + } + + @Override + protected void handleSecondaryClick(@Nullable View view) { + if (DEBUG) Log.d(TAG, "handleSecondaryClick"); + } + + @Override + public boolean isAvailable() { + return mWeatherClient.isOmniJawsServiceInstalled(); + } + + @Override + protected void handleClick(@Nullable View view) { + if (DEBUG) Log.d(TAG, "handleClick"); + if (!mWeatherClient.isOmniJawsSetupDone()) { + mActivityStarter.postStartActivityDismissingKeyguard(mWeatherClient.getSettingsIntent(), 0); + } else { + PackageManager pm = mContext.getPackageManager(); + for (String app: ALTERNATIVE_WEATHER_APPS) { + if (BootlegUtils.isPackageAvailable(mContext, app)) { + Intent intentThird = pm.getLaunchIntentForPackage(app); + if (intentThird != null) { + mActivityStarter.postStartActivityDismissingKeyguard(intentThird, 0); + break; + } + } + } + if (BootlegUtils.isPackageAvailable(mContext,"com.google.android.googlequicksearchbox")) { + Intent intent = new Intent(Intent.ACTION_VIEW); + intent.setData(Uri.parse("dynact://velour/weather/ProxyActivity")); + intent.setComponent(new ComponentName("com.google.android.googlequicksearchbox", + "com.google.android.apps.gsa.velour.DynamicActivityTrampoline")); + mActivityStarter.postStartActivityDismissingKeyguard(intent, 0); + } else { + mActivityStarter.postStartActivityDismissingKeyguard(mWeatherClient.getSettingsIntent(), 0); + } + } + } + + @Override + public Intent getLongClickIntent() { + if (DEBUG) Log.d(TAG, "getLongClickIntent"); + return mWeatherClient.getSettingsIntent(); + } + + @Override + protected void handleUpdateState(BooleanState state, Object arg) { + if (DEBUG) Log.d(TAG, "handleUpdateState " + mEnabled); + state.value = mEnabled; + state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE; + state.icon = mIcon; + state.label = mContext.getResources().getString(R.string.omnijaws_label_default); + if (mEnabled) { + if (mWeatherImage == null) { + state.secondaryLabel = mContext.getResources().getString(R.string.omnijaws_service_error_long); + } else { + state.icon = new DrawableIcon(mWeatherImage); + state.secondaryLabel = mWeatherLabel; + } + } else { + mWeatherImage = null; + state.secondaryLabel = mContext.getResources().getString(R.string.omnijaws_service_unkown); + } + } + + @Override + public CharSequence getTileLabel() { + return mContext.getResources().getString(R.string.omnijaws_label_default); + } + + private void queryAndUpdateWeather() { + try { + if (DEBUG) Log.d(TAG, "queryAndUpdateWeather " + mEnabled); + mWeatherImage = null; + mWeatherLabel = mContext.getResources().getString(R.string.omnijaws_label_default); + if (mEnabled) { + mWeatherClient.queryWeather(); + mWeatherData = mWeatherClient.getWeatherInfo(); + if (mWeatherData != null) { + mWeatherImage = mWeatherClient.getWeatherConditionImagefromPack( + mWeatherData.conditionCode, true /* force loading from default pack */); + mWeatherImage = mWeatherImage.mutate(); + mWeatherLabel = mWeatherData.temp + mWeatherData.tempUnits; + } else { + mWeatherLabel = mContext.getResources().getString(R.string.omnijaws_service_unkown); + } + } else { + mWeatherLabel = mContext.getResources().getString(R.string.omnijaws_label_default); + } + } catch(Exception e) { + mWeatherLabel = mContext.getResources().getString(R.string.omnijaws_label_default); + } + refreshState(); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java index 047762662aab..467e1dd5b002 100644 --- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java +++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java @@ -22,10 +22,12 @@ import android.app.NotificationManager; import android.app.PendingIntent; import android.app.Service; +import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.res.Resources; import android.graphics.Bitmap; +import android.graphics.PixelFormat; import android.graphics.drawable.Icon; import android.media.MediaRecorder; import android.net.Uri; @@ -36,6 +38,14 @@ import android.os.UserHandle; import android.provider.Settings; import android.util.Log; +import android.view.animation.AlphaAnimation; +import android.view.animation.Animation; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.WindowManager; +import android.widget.FrameLayout; +import android.widget.ImageView; import android.widget.Toast; import com.android.internal.annotations.VisibleForTesting; @@ -70,12 +80,15 @@ public class RecordingService extends Service implements ScreenMediaRecorderList private static final String EXTRA_AUDIO_SOURCE = "extra_useAudio"; private static final String EXTRA_SHOW_TAPS = "extra_showTaps"; private static final String EXTRA_CAPTURE_TARGET = "extra_captureTarget"; + private static final String EXTRA_SHOW_STOP_DOT = "extra_showStopDot"; + private static final String EXTRA_LONGER_DURATION = "extra_longerDuration"; private static final String ACTION_START = "com.android.systemui.screenrecord.START"; private static final String ACTION_STOP = "com.android.systemui.screenrecord.STOP"; private static final String ACTION_STOP_NOTIF = "com.android.systemui.screenrecord.STOP_FROM_NOTIF"; private static final String ACTION_SHARE = "com.android.systemui.screenrecord.SHARE"; + private static final String ACTION_DELETE = "com.android.systemui.screenrecord.DELETE"; private static final String PERMISSION_SELF = "com.android.systemui.permission.SELF"; private final RecordingController mController; @@ -90,6 +103,13 @@ public class RecordingService extends Service implements ScreenMediaRecorderList private final NotificationManager mNotificationManager; private final UserContextProvider mUserContextTracker; + private boolean mLongerDuration; + private boolean mShowStopDot; + private boolean mIsDotAtRight; + private boolean mDotShowing; + private FrameLayout mFrameLayout; + private WindowManager mWindowManager; + @Inject public RecordingService(RecordingController controller, @LongRunning Executor executor, @Main Handler handler, UiEventLogger uiEventLogger, @@ -101,6 +121,8 @@ public RecordingService(RecordingController controller, @LongRunning Executor ex mUiEventLogger = uiEventLogger; mNotificationManager = notificationManager; mUserContextTracker = userContextTracker; + mWindowManager = (WindowManager) userContextTracker.getUserContext() + .getSystemService(Context.WINDOW_SERVICE); mKeyguardDismissUtil = keyguardDismissUtil; } @@ -118,13 +140,16 @@ public RecordingService(RecordingController controller, @LongRunning Executor ex */ public static Intent getStartIntent(Context context, int resultCode, int audioSource, boolean showTaps, - @Nullable MediaProjectionCaptureTarget captureTarget) { + @Nullable MediaProjectionCaptureTarget captureTarget, + boolean showStopDot, boolean longerDuration) { return new Intent(context, RecordingService.class) .setAction(ACTION_START) .putExtra(EXTRA_RESULT_CODE, resultCode) .putExtra(EXTRA_AUDIO_SOURCE, audioSource) .putExtra(EXTRA_SHOW_TAPS, showTaps) - .putExtra(EXTRA_CAPTURE_TARGET, captureTarget); + .putExtra(EXTRA_CAPTURE_TARGET, captureTarget) + .putExtra(EXTRA_SHOW_STOP_DOT, showStopDot) + .putExtra(EXTRA_LONGER_DURATION, longerDuration); } @Override @@ -153,6 +178,11 @@ public int onStartCommand(Intent intent, int flags, int startId) { setTapsVisible(mShowTaps); + mShowStopDot = intent.getBooleanExtra(EXTRA_SHOW_STOP_DOT, false); + mLongerDuration = intent.getBooleanExtra(EXTRA_LONGER_DURATION, false); + + setStopDotVisible(mShowStopDot); + mRecorder = new ScreenMediaRecorder( mUserContextTracker.getUserContext(), mMainHandler, @@ -161,6 +191,7 @@ public int onStartCommand(Intent intent, int flags, int startId) { captureTarget, this ); + setLongerDuration(mLongerDuration); if (startRecording()) { updateState(true); @@ -207,6 +238,23 @@ public int onStartCommand(Intent intent, int flags, int startId) { // Close quick shade sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)); break; + case ACTION_DELETE: + // Close quick shade + sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)); + + ContentResolver resolver = getContentResolver(); + Uri uri = Uri.parse(intent.getStringExtra(EXTRA_PATH)); + resolver.delete(uri, null, null); + + Toast.makeText( + this, + R.string.screenrecord_delete_description, + Toast.LENGTH_LONG).show(); + + // Remove notification + mNotificationManager.cancelAsUser(null, NOTIFICATION_VIEW_ID, currentUser); + Log.d(TAG, "Deleted recording " + uri); + break; } return Service.START_STICKY; } @@ -363,6 +411,16 @@ protected Notification createSaveNotification(ScreenMediaRecorder.SavedRecording PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE)) .build(); + Notification.Action deleteAction = new Notification.Action.Builder( + Icon.createWithResource(this, R.drawable.ic_screenrecord), + getResources().getString(R.string.screenrecord_delete_label), + PendingIntent.getService( + this, + REQUEST_CODE, + getDeleteIntent(this, uri.toString()), + PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE)) + .build(); + Bundle extras = new Bundle(); extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME, getResources().getString(R.string.screenrecord_name)); @@ -377,6 +435,7 @@ protected Notification createSaveNotification(ScreenMediaRecorder.SavedRecording viewIntent, PendingIntent.FLAG_IMMUTABLE)) .addAction(shareAction) + .addAction(deleteAction) .setAutoCancel(true) .addExtras(extras); @@ -401,6 +460,7 @@ private void stopService(int userId) { } Log.d(TAG, "notifying for user " + userId); setTapsVisible(mOriginalShowTaps); + setStopDotVisible(false); if (getRecorder() != null) { try { getRecorder().end(); @@ -453,6 +513,88 @@ private void setTapsVisible(boolean turnOn) { Settings.System.putInt(getContentResolver(), Settings.System.SHOW_TOUCHES, value); } + private void setLongerDuration(boolean longer) { + if (getRecorder() != null) { + getRecorder().setLongerDuration(longer); + } + } + + private void setStopDotVisible(boolean turnOn) { + if (turnOn) { + showDot(); + } else if (mDotShowing) { + stopDot(); + } + } + + private void showDot() { + mDotShowing = true; + mIsDotAtRight = true; + final int size = (int) (this.getResources() + .getDimensionPixelSize(R.dimen.screenrecord_dot_size)); + final WindowManager.LayoutParams params = new WindowManager.LayoutParams( + WindowManager.LayoutParams.WRAP_CONTENT, + WindowManager.LayoutParams.WRAP_CONTENT, + WindowManager.LayoutParams.TYPE_PHONE, + WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE // don't get softkey inputs + | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL, // allow outside inputs + PixelFormat.TRANSLUCENT); + params.gravity = Gravity.TOP | Gravity.RIGHT; + params.width = size; + params.height = size; + + mFrameLayout = new FrameLayout(this); + + mWindowManager.addView(mFrameLayout, params); + final LayoutInflater inflater = + (LayoutInflater) mUserContextTracker.getUserContext() + .getSystemService(Context.LAYOUT_INFLATER_SERVICE); + inflater.inflate(R.layout.screenrecord_dot, mFrameLayout); + + final ImageView dot = (ImageView) mFrameLayout.findViewById(R.id.dot); + dot.setOnClickListener(v -> { + try { + getStopPendingIntent().send(); + } catch (PendingIntent.CanceledException e) {} + }); + + dot.setOnLongClickListener(v -> { + dot.setAnimation(null); + final WindowManager.LayoutParams layoutParams = + (WindowManager.LayoutParams) mFrameLayout.getLayoutParams(); + layoutParams.gravity = Gravity.TOP | (mIsDotAtRight? Gravity.LEFT : Gravity.RIGHT); + mIsDotAtRight = !mIsDotAtRight; + mWindowManager.updateViewLayout(mFrameLayout, layoutParams); + dot.startAnimation(getDotAnimation()); + return true; + }); + + dot.startAnimation(getDotAnimation()); + } + + private PendingIntent getStopPendingIntent() { + return PendingIntent.getService(this, REQUEST_CODE, getStopIntent(this), + PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); + } + + private void stopDot() { + mDotShowing = false; + final ImageView dot = (ImageView) mFrameLayout.findViewById(R.id.dot); + if (dot != null) { + dot.setAnimation(null); + mWindowManager.removeView(mFrameLayout); + } + } + + private Animation getDotAnimation() { + final Animation anim = new AlphaAnimation(0.0f, 1.0f); + anim.setDuration(500); + anim.setStartOffset(100); + anim.setRepeatMode(Animation.REVERSE); + anim.setRepeatCount(Animation.INFINITE); + return anim; + } + /** * Get an intent to stop the recording service. * @param context Context from the requesting activity @@ -478,6 +620,11 @@ private static Intent getShareIntent(Context context, String path) { .putExtra(EXTRA_PATH, path); } + private static Intent getDeleteIntent(Context context, String path) { + return new Intent(context, RecordingService.class).setAction(ACTION_DELETE) + .putExtra(EXTRA_PATH, path); + } + @Override public void onInfo(MediaRecorder mr, int what, int extra) { Log.d(TAG, "Media recorder info: " + what); diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java index b8d96f774e02..752f8bc85380 100644 --- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java +++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java @@ -40,6 +40,7 @@ import android.media.projection.MediaProjection; import android.media.projection.MediaProjectionManager; import android.net.Uri; +import android.os.Environment; import android.os.Handler; import android.os.IBinder; import android.os.RemoteException; @@ -52,6 +53,8 @@ import android.view.WindowManager; import com.android.systemui.media.MediaProjectionCaptureTarget; +import com.android.systemui.R; + import java.io.File; import java.io.Closeable; import java.io.IOException; @@ -73,6 +76,7 @@ public class ScreenMediaRecorder extends MediaProjection.Callback { private static final int AUDIO_SAMPLE_RATE = 44100; private static final int MAX_DURATION_MS = 60 * 60 * 1000; private static final long MAX_FILESIZE_BYTES = 5000000000L; + private static final long MAX_FILESIZE_BYTES_LONGER = 16106100000L; // 15 GiB private static final String TAG = "ScreenMediaRecorder"; @@ -88,6 +92,10 @@ public class ScreenMediaRecorder extends MediaProjection.Callback { private ScreenRecordingAudioSource mAudioSource; private final MediaProjectionCaptureTarget mCaptureRegion; private final Handler mHandler; + private int mMaxRefreshRate; + private String mAvcProfileLevel; + + private boolean mLongerDuration; private Context mContext; ScreenMediaRecorderListener mListener; @@ -102,6 +110,14 @@ public ScreenMediaRecorder(Context context, Handler handler, mCaptureRegion = captureRegion; mListener = listener; mAudioSource = audioSource; + mMaxRefreshRate = mContext.getResources().getInteger( + R.integer.config_screenRecorderMaxFramerate); + mAvcProfileLevel = mContext.getResources().getString( + R.string.config_screenRecorderAVCProfileLevel); + } + + public void setLongerDuration(boolean longer) { + mLongerDuration = longer; } private void prepare() throws IOException, RemoteException, RuntimeException { @@ -140,21 +156,23 @@ private void prepare() throws IOException, RemoteException, RuntimeException { WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE); wm.getDefaultDisplay().getRealMetrics(metrics); int refreshRate = (int) wm.getDefaultDisplay().getRefreshRate(); + if (mMaxRefreshRate != 0 && refreshRate > mMaxRefreshRate) refreshRate = mMaxRefreshRate; int[] dimens = getSupportedSize(metrics.widthPixels, metrics.heightPixels, refreshRate); int width = dimens[0]; int height = dimens[1]; refreshRate = dimens[2]; int vidBitRate = width * height * refreshRate / VIDEO_FRAME_RATE * VIDEO_FRAME_RATE_TO_RESOLUTION_RATIO; + long maxFilesize = mLongerDuration ? MAX_FILESIZE_BYTES_LONGER : MAX_FILESIZE_BYTES; mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264); mMediaRecorder.setVideoEncodingProfileLevel( - MediaCodecInfo.CodecProfileLevel.AVCProfileHigh, - MediaCodecInfo.CodecProfileLevel.AVCLevel3); + MediaCodecInfo.CodecProfileLevel.AVCProfileMain, + getAvcProfileLevelCodeByName(mAvcProfileLevel)); mMediaRecorder.setVideoSize(width, height); mMediaRecorder.setVideoFrameRate(refreshRate); mMediaRecorder.setVideoEncodingBitRate(vidBitRate); - mMediaRecorder.setMaxDuration(MAX_DURATION_MS); - mMediaRecorder.setMaxFileSize(MAX_FILESIZE_BYTES); + mMediaRecorder.setMaxDuration(mLongerDuration ? 0 : MAX_DURATION_MS); + mMediaRecorder.setMaxFileSize(maxFilesize); // Set up audio if (mAudioSource == MIC) { @@ -194,6 +212,21 @@ public void onStopped() { } + /** + * Match human-readable AVC level name to its constant value. + */ + private int getAvcProfileLevelCodeByName(final String levelName) { + switch (levelName) { + case "3": return MediaCodecInfo.CodecProfileLevel.AVCLevel3; + case "3.1": return MediaCodecInfo.CodecProfileLevel.AVCLevel31; + case "3.2": return MediaCodecInfo.CodecProfileLevel.AVCLevel32; + case "4": return MediaCodecInfo.CodecProfileLevel.AVCLevel4; + case "4.1": return MediaCodecInfo.CodecProfileLevel.AVCLevel41; + default: + case "4.2": return MediaCodecInfo.CodecProfileLevel.AVCLevel42; + } + } + /** * Find the highest supported screen resolution and refresh rate for the given dimensions on * this device, up to actual size and given rate. @@ -330,6 +363,7 @@ protected SavedRecording save() throws IOException { values.put(MediaStore.Video.Media.MIME_TYPE, "video/mp4"); values.put(MediaStore.Video.Media.DATE_ADDED, System.currentTimeMillis()); values.put(MediaStore.Video.Media.DATE_TAKEN, System.currentTimeMillis()); + values.put(MediaStore.Video.Media.RELATIVE_PATH, Environment.DIRECTORY_MOVIES + File.separator + "ScreenRecords"); ContentResolver resolver = mContext.getContentResolver(); Uri collectionUri = MediaStore.Video.Media.getContentUri( diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java index efa45a4c4723..1053dc2a833b 100644 --- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java +++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java @@ -44,6 +44,7 @@ import androidx.annotation.Nullable; +import com.android.systemui.Prefs; import com.android.systemui.R; import com.android.systemui.animation.ActivityLaunchAnimator; import com.android.systemui.animation.DialogLaunchAnimator; @@ -65,7 +66,15 @@ public class ScreenRecordDialog extends SystemUIDialog { private static final List MODES = Arrays.asList(INTERNAL, MIC, MIC_AND_INTERNAL); private static final long DELAY_MS = 3000; + private static final long NO_DELAY = 100; private static final long INTERVAL_MS = 1000; + private static final String PREFS = "screenrecord_"; + private static final String PREF_TAPS = "show_taps"; + private static final String PREF_DOT = "show_dot"; + private static final String PREF_LONGER = "use_longer_timeout"; + private static final String PREF_AUDIO = "use_audio"; + private static final String PREF_AUDIO_SOURCE = "audio_source"; + private static final String PREF_SKIP = "skip_timer"; private final RecordingController mController; private final UserContextProvider mUserContextProvider; @@ -75,7 +84,10 @@ public class ScreenRecordDialog extends SystemUIDialog { private final FeatureFlags mFlags; private final DialogLaunchAnimator mDialogLaunchAnimator; private Switch mTapsSwitch; + private Switch mStopDotSwitch; + private Switch mLongerSwitch; private Switch mAudioSwitch; + private Switch mSkipSwitch; private Spinner mOptions; public ScreenRecordDialog(Context context, RecordingController controller, @@ -147,6 +159,9 @@ public void onCreate(Bundle savedInstanceState) { mAudioSwitch = findViewById(R.id.screenrecord_audio_switch); mTapsSwitch = findViewById(R.id.screenrecord_taps_switch); + mSkipSwitch = findViewById(R.id.screenrecord_skip_switch); + mStopDotSwitch = findViewById(R.id.screenrecord_stopdot_switch); + mLongerSwitch = findViewById(R.id.screenrecord_longer_timeout_switch); mOptions = findViewById(R.id.screen_recording_options); ArrayAdapter a = new ScreenRecordingAdapter(getContext().getApplicationContext(), android.R.layout.simple_spinner_dropdown_item, @@ -156,6 +171,8 @@ public void onCreate(Bundle savedInstanceState) { mOptions.setOnItemClickListenerInt((parent, view, position, id) -> { mAudioSwitch.setChecked(true); }); + + loadPrefs(); } /** @@ -164,22 +181,27 @@ public void onCreate(Bundle savedInstanceState) { * null to record the whole screen */ private void requestScreenCapture(@Nullable MediaProjectionCaptureTarget captureTarget) { - Context userContext = mUserContextProvider.getUserContext(); + savePrefs(); boolean showTaps = mTapsSwitch.isChecked(); + boolean showStopDot = mStopDotSwitch.isChecked(); + boolean longerDuration = mLongerSwitch.isChecked(); + boolean skipTime = mSkipSwitch.isChecked(); ScreenRecordingAudioSource audioMode = mAudioSwitch.isChecked() ? (ScreenRecordingAudioSource) mOptions.getSelectedItem() : NONE; + Context userContext = mUserContextProvider.getUserContext(); PendingIntent startIntent = PendingIntent.getForegroundService(userContext, RecordingService.REQUEST_CODE, RecordingService.getStartIntent( userContext, Activity.RESULT_OK, - audioMode.ordinal(), showTaps, captureTarget), + audioMode.ordinal(), showTaps, captureTarget, + showStopDot, longerDuration), PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); PendingIntent stopIntent = PendingIntent.getService(userContext, RecordingService.REQUEST_CODE, RecordingService.getStopIntent(userContext), PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); - mController.startCountdown(DELAY_MS, INTERVAL_MS, startIntent, stopIntent); + mController.startCountdown(skipTime ? NO_DELAY : DELAY_MS, INTERVAL_MS, startIntent, stopIntent); } private class CaptureTargetResultReceiver extends ResultReceiver { @@ -199,4 +221,24 @@ protected void onReceiveResult(int resultCode, Bundle resultData) { } } } + + private void savePrefs() { + final Context userContext = mUserContextProvider.getUserContext(); + Prefs.putInt(userContext, PREFS + PREF_TAPS, mTapsSwitch.isChecked() ? 1 : 0); + Prefs.putInt(userContext, PREFS + PREF_DOT, mStopDotSwitch.isChecked() ? 1 : 0); + Prefs.putInt(userContext, PREFS + PREF_LONGER, mLongerSwitch.isChecked() ? 1 : 0); + Prefs.putInt(userContext, PREFS + PREF_AUDIO, mAudioSwitch.isChecked() ? 1 : 0); + Prefs.putInt(userContext, PREFS + PREF_AUDIO_SOURCE, mOptions.getSelectedItemPosition()); + Prefs.putInt(userContext, PREFS + PREF_SKIP, mSkipSwitch.isChecked() ? 1 : 0); + } + + private void loadPrefs() { + final Context userContext = mUserContextProvider.getUserContext(); + mTapsSwitch.setChecked(Prefs.getInt(userContext, PREFS + PREF_TAPS, 0) == 1); + mStopDotSwitch.setChecked(Prefs.getInt(userContext, PREFS + PREF_DOT, 0) == 1); + mLongerSwitch.setChecked(Prefs.getInt(userContext, PREFS + PREF_LONGER, 0) == 1); + mAudioSwitch.setChecked(Prefs.getInt(userContext, PREFS + PREF_AUDIO, 0) == 1); + mOptions.setSelection(Prefs.getInt(userContext, PREFS + PREF_AUDIO_SOURCE, 0)); + mSkipSwitch.setChecked(Prefs.getInt(userContext, PREFS + PREF_SKIP, 0) == 1); + } } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ImageExporter.java b/packages/SystemUI/src/com/android/systemui/screenshot/ImageExporter.java index 55602a98b8c5..dcd22958c54a 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ImageExporter.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ImageExporter.java @@ -60,6 +60,9 @@ class ImageExporter { // ex: 'Screenshot_20201215-090626.png' private static final String FILENAME_PATTERN = "Screenshot_%1$tY% exportToRawFile(Executor executor, Bitmap bitmap, * * @param executor the thread for execution * @param bitmap the bitmap to export + * @param foregroundAppName the name of app running in foreground * * @return a listenable future result */ - ListenableFuture export(Executor executor, UUID requestId, Bitmap bitmap) { - return export(executor, requestId, bitmap, ZonedDateTime.now()); + ListenableFuture export(Executor executor, UUID requestId, Bitmap bitmap, + String foregroundAppName) { + return export(executor, requestId, bitmap, ZonedDateTime.now(), foregroundAppName); } /** @@ -151,14 +156,15 @@ ListenableFuture export(Executor executor, UUID requestId, Bitmap bitmap * * @param executor the thread for execution * @param bitmap the bitmap to export + * @param foregroundAppName the name of app running in foreground * * @return a listenable future result */ ListenableFuture export(Executor executor, UUID requestId, Bitmap bitmap, - ZonedDateTime captureTime) { + ZonedDateTime captureTime, String foregroundAppName) { - final Task task = new Task(mResolver, requestId, bitmap, captureTime, mCompressFormat, - mQuality, /* publish */ true); + final Task task = new Task(mResolver, requestId, bitmap, captureTime, foregroundAppName, + mCompressFormat, mQuality, /* publish */ true); return CallbackToFutureAdapter.getFuture( (completer) -> { @@ -231,14 +237,14 @@ private static class Task { private final boolean mPublish; Task(ContentResolver resolver, UUID requestId, Bitmap bitmap, ZonedDateTime captureTime, - CompressFormat format, int quality, boolean publish) { + String foregroundAppName, CompressFormat format, int quality, boolean publish) { mResolver = resolver; mRequestId = requestId; mBitmap = bitmap; mCaptureTime = captureTime; mFormat = format; mQuality = quality; - mFileName = createFilename(mCaptureTime, mFormat); + mFileName = createFilename(mCaptureTime, mFormat, foregroundAppName); mPublish = publish; } @@ -377,7 +383,12 @@ private static void publishEntry(ContentResolver resolver, Uri uri) } @VisibleForTesting - static String createFilename(ZonedDateTime time, CompressFormat format) { + static String createFilename(ZonedDateTime time, CompressFormat format, + String foregroundAppName) { + if (foregroundAppName != null) { + return String.format(FILENAME_WITH_APP_NAME_PATTERN, time, foregroundAppName, + fileExtension(format)); + } return String.format(FILENAME_PATTERN, time, fileExtension(format)); } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java index ba6e98e79ac0..c8cffc560efd 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java @@ -387,7 +387,8 @@ private void startExport(PendingAction action) { mOutputBitmap = renderBitmap(drawable, bounds); ListenableFuture exportFuture = mImageExporter.export( - mBackgroundExecutor, UUID.randomUUID(), mOutputBitmap, ZonedDateTime.now()); + mBackgroundExecutor, UUID.randomUUID(), mOutputBitmap, ZonedDateTime.now(), + mLongScreenshotHolder.getForegroundAppName()); exportFuture.addListener(() -> onExportCompleted(action, exportFuture), mUiExecutor); } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotData.java b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotData.java index f549faf2414a..7b3c58e41384 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotData.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotData.java @@ -31,6 +31,7 @@ public class LongScreenshotData { private final AtomicReference mLongScreenshot; private final AtomicReference mTransitionDestinationCallback; + private String mForegroundAppName; @Inject public LongScreenshotData() { @@ -73,4 +74,18 @@ public void setTransitionDestinationCallback( public ScreenshotController.TransitionDestination takeTransitionDestinationCallback() { return mTransitionDestinationCallback.getAndSet(null); } + + /** + * Set the holder's foreground app name. + */ + public void setForegroundAppName(String foregroundAppName) { + mForegroundAppName = foregroundAppName; + } + + /** + * Return the current foreground app name. + */ + public String getForegroundAppName() { + return mForegroundAppName; + } } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java index f248d6913878..3285bda4e2cb 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java @@ -64,7 +64,7 @@ /** * An AsyncTask that saves an image to the media store in the background. */ -class SaveImageInBackgroundTask extends AsyncTask { +class SaveImageInBackgroundTask extends AsyncTask { private static final String TAG = logTag(SaveImageInBackgroundTask.class); private static final String SCREENSHOT_ID_TEMPLATE = "Screenshot_%s"; @@ -108,7 +108,7 @@ class SaveImageInBackgroundTask extends AsyncTask { } @Override - protected Void doInBackground(Void... paramsUnused) { + protected Void doInBackground(String... params) { if (isCancelled()) { if (DEBUG_STORAGE) { Log.d(TAG, "cancelled! returning null"); @@ -133,7 +133,8 @@ protected Void doInBackground(Void... paramsUnused) { // Call synchronously here since already on a background thread. ListenableFuture future = - mImageExporter.export(Runnable::run, requestId, image); + mImageExporter.export(Runnable::run, requestId, image, + params != null ? params[0] : null); ImageExporter.Result result = future.get(); final Uri uri = result.uri; mImageTime = result.timestamp; diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java index 3fee232b3465..48cc949ebd14 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java @@ -37,6 +37,7 @@ import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityOptions; +import android.app.ActivityTaskManager; import android.app.ExitTransitionCoordinator; import android.app.ExitTransitionCoordinator.ExitTransitionCallbacks; import android.app.ICompatCameraControlCallback; @@ -47,17 +48,25 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ActivityInfo; +import android.content.pm.PackageManager; import android.content.res.Configuration; import android.graphics.Bitmap; import android.graphics.Insets; import android.graphics.Rect; +import android.hardware.camera2.CameraManager; import android.hardware.display.DisplayManager; import android.media.AudioAttributes; +import android.media.AudioManager; import android.media.AudioSystem; +import android.media.MediaActionSound; import android.media.MediaPlayer; import android.net.Uri; import android.os.Bundle; import android.os.RemoteException; +import android.os.SystemProperties; +import android.os.UserHandle; +import android.os.VibrationEffect; +import android.os.Vibrator; import android.provider.Settings; import android.util.DisplayMetrics; import android.util.Log; @@ -73,6 +82,7 @@ import android.view.View; import android.view.ViewRootImpl; import android.view.ViewTreeObserver; +import android.view.Window; import android.view.WindowInsets; import android.view.WindowManager; import android.view.WindowManagerGlobal; @@ -92,6 +102,8 @@ import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.screenshot.ScreenshotController.SavedImageData.ActionTransition; import com.android.systemui.screenshot.TakeScreenshotService.RequestCallback; +import com.android.systemui.shared.system.TaskStackChangeListener; +import com.android.systemui.shared.system.TaskStackChangeListeners; import com.android.systemui.util.Assert; import com.google.common.util.concurrent.ListenableFuture; @@ -255,6 +267,10 @@ interface TransitionDestination { private final WindowManager.LayoutParams mWindowLayoutParams; private final AccessibilityManager mAccessibilityManager; private final ListenableFuture mCameraSound; + private final AudioManager mAudioManager; + private final Vibrator mVibrator; + private final CameraManager mCameraManager; + private int mCamsInUse = 0; private final ScrollCaptureClient mScrollCaptureClient; private final PhoneWindow mWindow; private final DisplayManager mDisplayManager; @@ -265,6 +281,8 @@ interface TransitionDestination { mScreenshotNotificationSmartActionsProvider; private final TimeoutHandler mScreenshotHandler; + private final FullScreenshotRunnable mFullScreenshotRunnable = new FullScreenshotRunnable(); + private ScreenshotView mScreenshotView; private Bitmap mScreenBitmap; private SaveImageInBackgroundTask mSaveInBgTask; @@ -285,6 +303,38 @@ interface TransitionDestination { | ActivityInfo.CONFIG_SCREEN_LAYOUT | ActivityInfo.CONFIG_ASSETS_PATHS); + private ComponentName mTaskComponentName; + private PackageManager mPm; + + private final TaskStackChangeListener mTaskListener = new TaskStackChangeListener() { + @Override + public void onTaskStackChanged() { + mBgExecutor.execute(() -> updateForegroundTaskSync()); + } + }; + + private void updateForegroundTaskSync() { + try { + final ActivityTaskManager.RootTaskInfo focusedStack = + ActivityTaskManager.getService().getFocusedRootTaskInfo(); + if (focusedStack != null && focusedStack.topActivity != null) { + mTaskComponentName = focusedStack.topActivity; + } + } catch (RemoteException e) { + Log.e(TAG, "Failed to get foreground task component", e); + } + } + + private String getForegroundAppLabel() { + if (mTaskComponentName != null) { + try { + final ActivityInfo ai = mPm.getActivityInfo(mTaskComponentName, 0); + return ai.applicationInfo.loadLabel(mPm).toString(); + } catch (PackageManager.NameNotFoundException e) {} + } + return null; + } + @Inject ScreenshotController( Context context, @@ -340,6 +390,10 @@ interface TransitionDestination { mWindow = FloatingWindowUtil.getFloatingWindow(mContext); mWindow.setWindowManager(mWindowManager, null, null); + mWindow.requestFeature(Window.FEATURE_NO_TITLE); + mWindow.requestFeature(Window.FEATURE_ACTIVITY_TRANSITIONS); + mWindow.setBackgroundDrawableResource(android.R.color.transparent); + mWindow.setDecorFitsSystemWindows(false); mConfigChanges.applyNewConfig(context.getResources()); reloadAssets(); @@ -347,6 +401,13 @@ interface TransitionDestination { // Setup the Camera shutter sound mCameraSound = loadCameraSound(); + // Grab system services needed for screenshot sound + mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); + mVibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE); + mCameraManager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE); + mCameraManager.registerAvailabilityCallback(mCamCallback, + timeoutHandler); + mCopyBroadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -359,12 +420,35 @@ public void onReceive(Context context, Intent intent) { mContext.registerReceiver(mCopyBroadcastReceiver, new IntentFilter( ClipboardOverlayController.COPY_OVERLAY_ACTION), ClipboardOverlayController.SELF_PERMISSION, null, Context.RECEIVER_NOT_EXPORTED); + + // Grab PackageManager + mPm = mContext.getPackageManager(); + + // Register task stack listener + TaskStackChangeListeners.getInstance().registerTaskStackListener(mTaskListener); + + // Initialize current foreground package name + updateForegroundTaskSync(); } @MainThread void takeScreenshotFullscreen(ComponentName topComponent, Consumer finisher, RequestCallback requestCallback) { Assert.isMainThread(); + mScreenshotHandler.removeCallbacks(mFullScreenshotRunnable); + /** + * Do not let it run the finish callback, it'll reset the service + * connection and break the next screenshot. + */ + mCurrentRequestCallback = null; + dismissScreenshot(true); + mFullScreenshotRunnable.setArgs(topComponent, finisher, requestCallback); + // Wait 50ms to make sure we are on new frame. + mScreenshotHandler.postDelayed(mFullScreenshotRunnable, 50); + } + + void takeScreenshotFullscreenInternal(ComponentName topComponent, Consumer finisher, + RequestCallback requestCallback) { mCurrentRequestCallback = requestCallback; DisplayMetrics displayMetrics = new DisplayMetrics(); getDefaultDisplay().getRealMetrics(displayMetrics); @@ -448,6 +532,7 @@ void onDestroy() { removeWindow(); releaseMediaPlayer(); releaseContext(); + TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mTaskListener); mBgExecutor.shutdownNow(); } @@ -498,8 +583,7 @@ public void onDismiss() { @Override public void onTouchOutside() { - // TODO(159460485): Remove this when focus is handled properly in the system - setWindowFocusable(false); + dismissScreenshot(false); } }); mScreenshotView.setDefaultTimeoutMillis(mScreenshotHandler.getDefaultTimeoutMillis()); @@ -743,6 +827,8 @@ private void runBatchScrollCapture(ScrollCaptureResponse response) { transitionDestination, onTransitionEnd, longScreenshot)); + mLongScreenshotHolder.setForegroundAppName(getForegroundAppLabel()); + final Intent intent = new Intent(mContext, LongScreenshotActivity.class); intent.setFlags( Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); @@ -855,7 +941,7 @@ private void playCameraSound() { */ private void saveScreenshotAndToast(Consumer finisher) { // Play the shutter sound to notify that we've taken a screenshot - playCameraSound(); + playShutterSound(); saveScreenshotInWorkerThread( /* onComplete */ finisher, @@ -889,7 +975,7 @@ private void startAnimation(Rect screenRect, boolean showFlash) { mScreenshotView.createScreenshotDropInAnimation(screenRect, showFlash); // Play the shutter sound to notify that we've taken a screenshot - playCameraSound(); + playShutterSound(); if (DEBUG_ANIM) { Log.d(TAG, "starting post-screenshot animation"); @@ -944,7 +1030,7 @@ private void saveScreenshotInWorkerThread(Consumer finisher, mSaveInBgTask = new SaveImageInBackgroundTask(mContext, mImageExporter, mScreenshotSmartActions, data, getActionTransitionSupplier(), mScreenshotNotificationSmartActionsProvider); - mSaveInBgTask.execute(); + mSaveInBgTask.execute(getForegroundAppLabel()); } @@ -1128,4 +1214,66 @@ public void onFinish() { }; } } + + private void playShutterSound() { + boolean playSound = readCameraSoundForced() && mCamsInUse > 0; + switch (mAudioManager.getRingerMode()) { + case AudioManager.RINGER_MODE_SILENT: + // do nothing + break; + case AudioManager.RINGER_MODE_VIBRATE: + if (mVibrator != null && mVibrator.hasVibrator()) { + mVibrator.vibrate(VibrationEffect.createOneShot(50, + VibrationEffect.DEFAULT_AMPLITUDE)); + } + break; + case AudioManager.RINGER_MODE_NORMAL: + // in this case we want to play sound even if not forced on + playSound = true; + break; + } + // We want to play the shutter sound when it's either forced or + // when we use normal ringer mode + if (playSound && Settings.System.getIntForUser(mContext.getContentResolver(), + Settings.System.SCREENSHOT_SHUTTER_SOUND, 1, UserHandle.USER_CURRENT) == 1) { + playCameraSound(); + } + } + + private CameraManager.AvailabilityCallback mCamCallback = + new CameraManager.AvailabilityCallback() { + @Override + public void onCameraOpened(String cameraId, String packageId) { + mCamsInUse++; + } + + @Override + public void onCameraClosed(String cameraId) { + mCamsInUse--; + } + }; + + private boolean readCameraSoundForced() { + return SystemProperties.getBoolean("audio.camerasound.force", false) || + mContext.getResources().getBoolean( + com.android.internal.R.bool.config_camera_sound_forced); + } + + private class FullScreenshotRunnable implements Runnable { + ComponentName mTopComponent; + Consumer mFinisher; + RequestCallback mRequestCallback; + + public void setArgs(ComponentName topComponent, Consumer finisher, + RequestCallback requestCallback) { + mTopComponent = topComponent; + mFinisher = finisher; + mRequestCallback = requestCallback; + } + + @Override + public void run() { + takeScreenshotFullscreenInternal(mTopComponent, mFinisher, mRequestCallback); + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java index 8b5a24c0e2ff..218428bb9183 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java @@ -52,6 +52,8 @@ public enum ScreenshotEvent implements UiEventLogger.UiEventEnum { SCREENSHOT_EDIT_TAPPED(308), @UiEvent(doc = "screenshot share button tapped") SCREENSHOT_SHARE_TAPPED(309), + @UiEvent(doc = "screenshot delete button tapped") + SCREENSHOT_DELETE_TAPPED(369), @UiEvent(doc = "screenshot smart action chip tapped") SCREENSHOT_SMART_ACTION_TAPPED(374), @UiEvent(doc = "screenshot scroll tapped") diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java index 360fc879731c..7231793f3208 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java @@ -119,8 +119,8 @@ interface ScreenshotViewCallback { private static final long SCREENSHOT_TO_CORNER_X_DURATION_MS = 234; private static final long SCREENSHOT_TO_CORNER_Y_DURATION_MS = 500; private static final long SCREENSHOT_TO_CORNER_SCALE_DURATION_MS = 234; - private static final long SCREENSHOT_ACTIONS_EXPANSION_DURATION_MS = 400; - private static final long SCREENSHOT_ACTIONS_ALPHA_DURATION_MS = 100; + private static final long SCREENSHOT_ACTIONS_EXPANSION_DURATION_MS = 300; + private static final long SCREENSHOT_ACTIONS_ALPHA_DURATION_MS = 75; private static final long SCREENSHOT_DISMISS_X_DURATION_MS = 350; private static final long SCREENSHOT_DISMISS_ALPHA_DURATION_MS = 350; private static final long SCREENSHOT_DISMISS_ALPHA_OFFSET_MS = 50; // delay before starting fade @@ -156,6 +156,7 @@ interface ScreenshotViewCallback { private OverlayActionChip mEditChip; private OverlayActionChip mScrollChip; private OverlayActionChip mQuickShareChip; + private OverlayActionChip mDeleteChip; private UiEventLogger mUiEventLogger; private ScreenshotViewCallback mCallbacks; @@ -174,6 +175,7 @@ interface ScreenshotViewCallback { private enum PendingInteraction { PREVIEW, EDIT, + DELETE, SHARE, QUICK_SHARE } @@ -365,6 +367,7 @@ protected void onFinishInflate() { mShareChip = requireNonNull(mActionsContainer.findViewById(R.id.screenshot_share_chip)); mEditChip = requireNonNull(mActionsContainer.findViewById(R.id.screenshot_edit_chip)); mScrollChip = requireNonNull(mActionsContainer.findViewById(R.id.screenshot_scroll_chip)); + mDeleteChip = requireNonNull(mActionsContainer.findViewById(R.id.screenshot_delete_chip)); int swipePaddingPx = (int) FloatingWindowUtil.dpToPx(mDisplayMetrics, SWIPE_PADDING_DP); TouchDelegate previewDelegate = new TouchDelegate( @@ -677,6 +680,7 @@ ValueAnimator createScreenshotActionsShadeAnimation() { if (mQuickShareChip != null) { mQuickShareChip.setIsPending(false); } + mDeleteChip.setIsPending(false); mPendingInteraction = PendingInteraction.SHARE; }); chips.add(mShareChip); @@ -691,16 +695,31 @@ ValueAnimator createScreenshotActionsShadeAnimation() { if (mQuickShareChip != null) { mQuickShareChip.setIsPending(false); } + mDeleteChip.setIsPending(false); mPendingInteraction = PendingInteraction.EDIT; }); chips.add(mEditChip); + mDeleteChip.setContentDescription(mContext.getString(R.string.screenshot_delete_label)); + mDeleteChip.setIcon(Icon.createWithResource(mContext, R.drawable.ic_screenshot_delete), true); + mDeleteChip.setOnClickListener(v -> { + mDeleteChip.setIsPending(true); + mEditChip.setIsPending(false); + mShareChip.setIsPending(false); + if (mQuickShareChip != null) { + mQuickShareChip.setIsPending(false); + } + mPendingInteraction = PendingInteraction.DELETE; + }); + chips.add(mDeleteChip); + mScreenshotPreview.setOnClickListener(v -> { mShareChip.setIsPending(false); mEditChip.setIsPending(false); if (mQuickShareChip != null) { mQuickShareChip.setIsPending(false); } + mDeleteChip.setIsPending(false); mPendingInteraction = PendingInteraction.PREVIEW; }); @@ -778,6 +797,10 @@ void setChipIntents(ScreenshotController.SavedImageData imageData) { startSharedTransition( imageData.editTransition.get()); }); + mDeleteChip.setPendingIntent(imageData.deleteAction.actionIntent, () -> { + mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_DELETE_TAPPED); + animateDismissal(); + }); mScreenshotPreview.setOnClickListener(v -> { mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_PREVIEW_TAPPED, 0, mPackageName); startSharedTransition( @@ -803,6 +826,9 @@ void setChipIntents(ScreenshotController.SavedImageData imageData) { case EDIT: mEditChip.callOnClick(); break; + case DELETE: + mDeleteChip.callOnClick(); + break; case QUICK_SHARE: mQuickShareChip.callOnClick(); break; @@ -838,6 +864,7 @@ void addQuickShareChip(Notification.Action quickShareAction) { mQuickShareChip.setOnClickListener(v -> { mShareChip.setIsPending(false); mEditChip.setIsPending(false); + mDeleteChip.setIsPending(false); mQuickShareChip.setIsPending(true); mPendingInteraction = PendingInteraction.QUICK_SHARE; }); @@ -1021,8 +1048,10 @@ void reset() { mShareChip.setOnClickListener(null); mScrollingScrim.setVisibility(View.GONE); mEditChip.setOnClickListener(null); + mDeleteChip.setOnClickListener(null); mShareChip.setIsPending(false); mEditChip.setIsPending(false); + mDeleteChip.setIsPending(false); mPendingInteraction = null; for (OverlayActionChip chip : mSmartChips) { mActionsView.removeView(chip); diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt index 47bed461e371..97224cc3a52e 100644 --- a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt @@ -31,6 +31,7 @@ import androidx.annotation.WorkerThread import com.android.systemui.Dumpable import com.android.systemui.dump.DumpManager import com.android.systemui.util.Assert +import ink.kaleidoscope.ParallelSpaceManager; import java.io.PrintWriter import java.lang.IllegalStateException import java.lang.ref.WeakReference @@ -114,6 +115,7 @@ class UserTrackerImpl internal constructor( addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED) addAction(Intent.ACTION_MANAGED_PROFILE_UNLOCKED) + addAction(Intent.ACTION_PARALLEL_SPACE_CHANGED) } context.registerReceiverForAllUsers(this, filter, null /* permission */, backgroundHandler) @@ -128,7 +130,8 @@ class UserTrackerImpl internal constructor( Intent.ACTION_MANAGED_PROFILE_AVAILABLE, Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE, Intent.ACTION_MANAGED_PROFILE_REMOVED, - Intent.ACTION_MANAGED_PROFILE_UNLOCKED -> { + Intent.ACTION_MANAGED_PROFILE_UNLOCKED, + Intent.ACTION_PARALLEL_SPACE_CHANGED -> { handleProfilesChanged() } } @@ -142,6 +145,7 @@ class UserTrackerImpl internal constructor( private fun setUserIdInternal(user: Int): Pair> { val profiles = userManager.getProfiles(user) + profiles.addAll(ParallelSpaceManager.getInstance().getParallelUsers()) val handle = UserHandle(user) val ctx = context.createContextAsUser(handle, 0) @@ -178,6 +182,7 @@ class UserTrackerImpl internal constructor( Assert.isNotMainThread() val profiles = userManager.getProfiles(userId) + profiles.addAll(ParallelSpaceManager.getInstance().getParallelUsers()) synchronized(mutex) { userProfiles = profiles.map { UserInfo(it) } // save a "deep" copy } @@ -255,4 +260,4 @@ private data class DataItem( fun sameOrEmpty(other: UserTracker.Callback): Boolean { return callback.get()?.equals(other) ?: true } -} \ No newline at end of file +} diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java index 7801c68586f1..4cf73da49294 100644 --- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java +++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java @@ -41,6 +41,7 @@ import android.service.vr.IVrStateCallbacks; import android.util.Log; import android.util.MathUtils; +import android.widget.ImageView; import com.android.internal.display.BrightnessSynchronizer; import com.android.internal.logging.MetricsLogger; @@ -57,6 +58,7 @@ public class BrightnessController implements ToggleSlider.Listener, MirroredBrig private static final String TAG = "CentralSurfaces.BrightnessController"; private static final int SLIDER_ANIMATION_DURATION = 3000; + private static final int MSG_UPDATE_ICON = 0; private static final int MSG_UPDATE_SLIDER = 1; private static final int MSG_ATTACH_LISTENER = 2; private static final int MSG_DETACH_LISTENER = 3; @@ -70,6 +72,7 @@ public class BrightnessController implements ToggleSlider.Listener, MirroredBrig private final float mMinimumBacklightForVr; private final float mMaximumBacklightForVr; + private final ImageView mIcon; private final int mDisplayId; private final Context mContext; private final ToggleSlider mControl; @@ -216,6 +219,7 @@ public void run() { Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL, UserHandle.USER_CURRENT); mAutomatic = automatic != Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL; + mHandler.obtainMessage(MSG_UPDATE_ICON, mAutomatic ? 1 : 0).sendToTarget(); } }; @@ -254,6 +258,9 @@ public void handleMessage(Message msg) { mExternalChange = true; try { switch (msg.what) { + case MSG_UPDATE_ICON: + updateIcon(msg.arg1 != 0); + break; case MSG_UPDATE_SLIDER: updateSlider(Float.intBitsToFloat(msg.arg1), msg.arg2 != 0); break; @@ -303,6 +310,13 @@ public void onUserSwitched(int newUserId) { mDisplayManager = context.getSystemService(DisplayManager.class); mVrManager = IVrManager.Stub.asInterface(ServiceManager.getService( Context.VR_SERVICE)); + + mIcon = control.getIcon(); + mIcon.setOnClickListener(v -> Settings.System.putIntForUser(mContext.getContentResolver(), + Settings.System.SCREEN_BRIGHTNESS_MODE, mAutomatic ? + Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL : + Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC, + UserHandle.USER_CURRENT)); } public void registerCallbacks() { @@ -317,6 +331,7 @@ public void unregisterCallbacks() { @Override public void onChanged(boolean tracking, int value, boolean stopTracking) { + updateIcon(mAutomatic); if (mExternalChange) return; if (mSliderAnimator != null) { @@ -381,6 +396,14 @@ private void setBrightness(float brightness) { mDisplayManager.setTemporaryBrightness(mDisplayId, brightness); } + private void updateIcon(boolean automatic) { + if (mIcon != null) { + mIcon.setImageResource(mAutomatic ? + com.android.systemui.R.drawable.ic_qs_brightness_auto_on : + com.android.systemui.R.drawable.ic_qs_brightness_auto_off); + } + } + private void updateVrMode(boolean isEnabled) { if (mIsVrModeEnabled != isEnabled) { mIsVrModeEnabled = isEnabled; diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java index 6c8190af27f7..d6db4d4db6ef 100644 --- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java +++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderController.java @@ -21,6 +21,7 @@ import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; +import android.widget.ImageView; import android.widget.SeekBar; import androidx.annotation.Nullable; @@ -49,6 +50,7 @@ public class BrightnessSliderController extends ViewController mKeyguardBottomArea.setVisibility(View.GONE); private final AccessibilityDelegate mAccessibilityDelegate = new AccessibilityDelegate() { + @Override public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) { @@ -903,7 +940,18 @@ public NotificationPanelViewController(NotificationPanelView view, mQsFrameTranslateController = qsFrameTranslateController; updateUserSwitcherFlags(); mKeyguardBottomAreaViewModel = keyguardBottomAreaViewModel; + + mDoubleTapToSleepGesture = new GestureDetector(mView.getContext(), + new GestureDetector.SimpleOnGestureListener() { + @Override + public boolean onDoubleTap(MotionEvent e) { + BootlegUtils.switchScreenOff(mView.getContext()); + return true; + } + }); + onFinishInflate(); + mAppExceptions = mResources.getStringArray(R.array.app_exceptions); keyguardUnlockAnimationController.addKeyguardUnlockAnimationListener( new KeyguardUnlockAnimationController.KeyguardUnlockAnimationListener() { @Override @@ -978,13 +1026,6 @@ void onFinishInflate() { } } - mKeyguardStatusBarViewController = - mKeyguardStatusBarViewComponentFactory.build( - mKeyguardStatusBar, - mNotificationPanelViewStateProvider) - .getKeyguardStatusBarViewController(); - mKeyguardStatusBarViewController.init(); - mNotificationContainerParent = mView.findViewById(R.id.notification_container_parent); updateViewControllers( mView.findViewById(R.id.keyguard_status_view), @@ -1005,6 +1046,11 @@ void onFinishInflate() { addTrackingHeadsUpListener(mNotificationStackScrollLayoutController::setTrackingHeadsUp); mKeyguardBottomArea = mView.findViewById(R.id.keyguard_bottom_area); + mReTickerComeback = mView.findViewById(R.id.ticker_comeback); + mReTickerComebackIcon = mView.findViewById(R.id.ticker_comeback_icon); + mReTickerContentTV = mView.findViewById(R.id.ticker_content); + mNotificationStackScroller = mView.findViewById(R.id.notification_stack_scroller); + initBottomArea(); mWakeUpCoordinator.setStackScroller(mNotificationStackScrollLayoutController); @@ -1071,6 +1117,8 @@ protected void loadDimens() { mUdfpsMaxYBurnInOffset = mResources.getDimensionPixelSize(R.dimen.udfps_burn_in_offset_y); mSplitShadeScrimTransitionDistance = mResources.getDimensionPixelSize( R.dimen.split_shade_scrim_transition_distance); + mStatusBarHeaderHeight = mResources.getDimensionPixelSize( + R.dimen.status_bar_height); } private void updateViewControllers(KeyguardStatusView keyguardStatusView, @@ -1082,6 +1130,13 @@ private void updateViewControllers(KeyguardStatusView keyguardStatusView, mKeyguardStatusViewController = statusViewComponent.getKeyguardStatusViewController(); mKeyguardStatusViewController.init(); + mKeyguardStatusBarViewController = + mKeyguardStatusBarViewComponentFactory.build( + mKeyguardStatusBar, + mNotificationPanelViewStateProvider) + .getKeyguardStatusBarViewController(); + mKeyguardStatusBarViewController.init(); + if (mKeyguardUserSwitcherController != null) { // Try to close the switcher so that callbacks are triggered if necessary. // Otherwise, NPV can get into a state where some of the views are still hidden @@ -1208,6 +1263,15 @@ void reInflateViews() { keyguardStatusView = (KeyguardStatusView) mLayoutInflater.inflate( R.layout.keyguard_status_view, mNotificationContainerParent, false); mNotificationContainerParent.addView(keyguardStatusView, statusIndex); + + // Re-inflate the keyguard status bar. + statusIndex = mView.indexOfChild(mKeyguardStatusBar); + mView.removeView(mKeyguardStatusBar); + mKeyguardStatusBar = (KeyguardStatusBarView) mLayoutInflater.inflate( + R.layout.keyguard_status_bar, mView, false); + mView.addView(mKeyguardStatusBar, statusIndex); + mKeyguardStatusBar.setVisibility(isOnKeyguard() ? View.VISIBLE : View.INVISIBLE); + // When it's reinflated, this is centered by default. If it shouldn't be, this will update // below when resources are updated. mStatusViewCentered = true; @@ -2488,6 +2552,7 @@ private void updateQsExpansion() { mLargeScreenShadeHeaderController.setShadeExpandedFraction(shadeExpandedFraction); mLargeScreenShadeHeaderController.setQsExpandedFraction(qsExpansionFraction); mLargeScreenShadeHeaderController.setQsVisible(mQsVisible); + reTickerViewVisibility(); } private void onStackYChanged(boolean shouldAnimate) { @@ -3886,6 +3951,10 @@ public void setAmbientIndicationTop(int ambientIndicationTop, boolean ambientTex } } + private boolean isLandscape() { + return mView.getContext().getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE; + } + public void dozeTimeTick() { mLockIconViewController.dozeTimeTick(); mKeyguardStatusViewController.dozeTimeTick(); @@ -4123,6 +4192,14 @@ public void initDependencies( updateMaxDisplayedNotifications(true); } + public void setLockscreenDoubleTapToSleep(boolean isDoubleTapEnabled) { + mIsLockscreenDoubleTapEnabled = isDoubleTapEnabled; + } + + public void setSbDoubleTapToSleep(boolean isDoubleTapEnabled) { + mIsSbDoubleTapEnabled = isDoubleTapEnabled; + } + public void setAlpha(float alpha) { mView.setAlpha(alpha); } @@ -4253,6 +4330,14 @@ public boolean onTouch(View v, MotionEvent event) { if (mLastEventSynthesizedDown && event.getAction() == MotionEvent.ACTION_UP) { expand(true /* animate */); } + + if ((mIsLockscreenDoubleTapEnabled && !mPulsing && !mDozing + && mBarState == StatusBarState.KEYGUARD) || + (!mQsExpanded && mIsSbDoubleTapEnabled + && event.getY() < mStatusBarHeaderHeight)) { + mDoubleTapToSleepGesture.onTouchEvent(event); + } + initDownStates(event); // If pulse is expanding already, let's give it the touch. There are situations @@ -4384,6 +4469,30 @@ private void registerSettingsChangeListener() { /* notifyForDescendants */ false, mSettingsChangeObserver ); + mContentResolver.registerContentObserver( + Settings.System.getUriFor(Settings.System.KEYGUARD_QUICK_TOGGLES), + /* notifyForDescendants */ false, + mSettingsChangeObserver + ); + mContentResolver.registerContentObserver( + Settings.System.getUriFor(Settings.System.RETICKER_STATUS), + /* notifyForDescendants */ false, + mSettingsChangeObserver, + UserHandle.USER_ALL + ); + mContentResolver.registerContentObserver( + Settings.System.getUriFor(Settings.System.RETICKER_COLORED), + /* notifyForDescendants */ false, + mSettingsChangeObserver, + UserHandle.USER_ALL + ); + mContentResolver.registerContentObserver( + Settings.System.getUriFor(Settings.System.RETICKER_LANDSCAPE_ONLY), + /* notifyForDescendants */ false, + mSettingsChangeObserver, + UserHandle.USER_ALL + ); + updateReticker(); } private void unregisterSettingsChangeListener() { @@ -4610,14 +4719,37 @@ private class SettingsChangeObserver extends ContentObserver { } @Override - public void onChange(boolean selfChange) { + public void onChange(boolean selfChange, Uri uri) { if (DEBUG_LOGCAT) Log.d(TAG, "onSettingsChanged"); - // Can affect multi-user switcher visibility - reInflateViews(); + if (uri.getLastPathSegment().equals( + Settings.System.KEYGUARD_QUICK_TOGGLES)) { + mKeyguardBottomAreaViewModel.updateSettings(); + } + + if (uri.equals(Settings.System.getUriFor( + Settings.System.RETICKER_STATUS)) + || uri.equals(Settings.System.getUriFor( + Settings.System.RETICKER_COLORED)) + || uri.equals(Settings.System.getUriFor( + Settings.System.RETICKER_LANDSCAPE_ONLY))) { + updateReticker(); + } else { + // Can affect multi-user switcher visibility + reInflateViews(); + } } } + private void updateReticker() { + mReTickerStatus = Settings.System.getIntForUser(mView.getContext().getContentResolver(), + Settings.System.RETICKER_STATUS, 0, UserHandle.USER_CURRENT) != 0; + mReTickerColored = Settings.System.getIntForUser(mView.getContext().getContentResolver(), + Settings.System.RETICKER_COLORED, 0, UserHandle.USER_CURRENT) != 0; + mReTickerLandscapeOnly = Settings.System.getIntForUser(mView.getContext().getContentResolver(), + Settings.System.RETICKER_LANDSCAPE_ONLY, 0, UserHandle.USER_CURRENT) != 0; + } + private class StatusBarStateListener implements StateListener { @Override public void onStateChanged(int statusBarState) { @@ -4688,6 +4820,9 @@ public void onStateChanged(int statusBarState) { /* alpha= */ 1f, keyguardShowing ? View.VISIBLE : View.INVISIBLE); } + if (keyguardShowing) { + mKeyguardStatusBar.toggleContents(true); + } if (keyguardShowing && oldState != mBarState) { if (mQs != null) { mQs.hideImmediately(); @@ -5069,4 +5204,102 @@ private void notifyExpandImmediateChange(boolean expandImmediateEnabled) { } } } + + /* reTicker */ + + public void reTickerView(boolean visibility) { + if (!mReTickerStatus || mReTickerLandscapeOnly && !isLandscape()) return; + if (visibility && mReTickerComeback.getVisibility() == View.VISIBLE) { + reTickerDismissal(); + } + String reTickerContent; + if (visibility && getExpandedFraction() != 1) { + mNotificationStackScroller.setVisibility(View.GONE); + StatusBarNotification sbn = mHeadsUpManager.getTopEntry().getRow().getEntry().getSbn(); + Notification notification = sbn.getNotification(); + String pkgname = sbn.getPackageName(); + Drawable icon = null; + try { + if (pkgname.equals("com.android.systemui")) { + icon = mView.getContext().getDrawable(notification.icon); + } else { + icon = mView.getContext().getPackageManager().getApplicationIcon(pkgname); + } + } catch (NameNotFoundException e) { + return; + } + String content = notification.extras.getString("android.text"); + if (TextUtils.isEmpty(content)) return; + reTickerContent = content; + String reTickerAppName = notification.extras.getString("android.title"); + PendingIntent reTickerIntent = notification.contentIntent; + String mergedContentText = reTickerAppName + " " + reTickerContent; + mReTickerComebackIcon.setImageDrawable(icon); + Drawable dw = mView.getContext().getDrawable(R.drawable.reticker_background); + if (mReTickerColored) { + int col = notification.color; + // check if we need to override the color + if ((mAppExceptions.length & 1) == 0) { + for (int i = 0; i < mAppExceptions.length; i += 2) { + if (mAppExceptions[i].equals(pkgname)) { + col = Color.parseColor(mAppExceptions[i + 1]); + break; + } + } + } + dw.setTint(col); + } else { + dw.setTintList(null); + } + mReTickerComeback.setBackground(dw); + mReTickerContentTV.setText(mergedContentText); + mReTickerContentTV.setTextAppearance(mView.getContext(), R.style.TextAppearance_Notifications_reTicker); + mReTickerContentTV.setSelected(true); + RetickerAnimations.doBounceAnimationIn(mReTickerComeback); + if (reTickerIntent != null) { + mReTickerComeback.setOnClickListener(v -> { + try { + reTickerIntent.send(); + } catch (PendingIntent.CanceledException e) { + } + RetickerAnimations.doBounceAnimationOut(mReTickerComeback, mNotificationStackScroller); + reTickerViewVisibility(); + }); + } + } else { + reTickerDismissal(); + } + } + + private void reTickerViewVisibility() { + if (!mReTickerStatus || mReTickerLandscapeOnly && !isLandscape()) { + reTickerDismissal(); + return; + } + mNotificationStackScroller.setVisibility(getExpandedFraction() == 0 ? View.GONE : View.VISIBLE); + if (getExpandedFraction() > 0) mReTickerComeback.setVisibility(View.GONE); + if (mReTickerComeback.getVisibility() == View.VISIBLE) { + mReTickerComeback.getViewTreeObserver().addOnComputeInternalInsetsListener(mInsetsListener); + } else { + mReTickerComeback.getViewTreeObserver().removeOnComputeInternalInsetsListener(mInsetsListener); + } + } + + public void reTickerDismissal() { + RetickerAnimations.doBounceAnimationOut(mReTickerComeback, mNotificationStackScroller); + mReTickerComeback.getViewTreeObserver().removeOnComputeInternalInsetsListener(mInsetsListener); + } + + private final OnComputeInternalInsetsListener mInsetsListener = internalInsetsInfo -> { + internalInsetsInfo.touchableRegion.setEmpty(); + internalInsetsInfo.setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_REGION); + int[] mainLocation = new int[2]; + mReTickerComeback.getLocationOnScreen(mainLocation); + internalInsetsInfo.touchableRegion.set(new Region( + mainLocation[0], + mainLocation[1], + mainLocation[0] + mReTickerComeback.getWidth(), + mainLocation[1] + mReTickerComeback.getHeight() + )); + }; } diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java index 6be9bbbf4e0d..8181cd7e33c4 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java @@ -19,7 +19,9 @@ import android.app.StatusBarManager; import android.media.AudioManager; import android.media.session.MediaSessionLegacyHelper; +import android.provider.Settings; import android.os.SystemClock; +import android.os.UserHandle; import android.util.Log; import android.view.GestureDetector; import android.view.InputDevice; @@ -454,4 +456,18 @@ public void setStatusBarViewController(PhoneStatusBarViewController statusBarVie void setDragDownHelper(DragDownHelper dragDownHelper) { mDragDownHelper = dragDownHelper; } + + public void setDoubleTapToSleepGesture() { + boolean isDoubleTapLockscreenEnabled = Settings.System.getIntForUser(mView.getContext().getContentResolver(), + Settings.System.DOUBLE_TAP_SLEEP_LOCKSCREEN, 0, UserHandle.USER_CURRENT) == 1; + boolean isDoubleTapSbEnabled = Settings.System.getIntForUser(mView.getContext().getContentResolver(), + Settings.System.DOUBLE_TAP_SLEEP_GESTURE, 0, UserHandle.USER_CURRENT) == 1; + if (mNotificationPanelViewController != null) { + mNotificationPanelViewController.setLockscreenDoubleTapToSleep(isDoubleTapLockscreenEnabled); + mNotificationPanelViewController.setSbDoubleTapToSleep(isDoubleTapSbEnabled); + } + if (mDragDownHelper != null) { + mDragDownHelper.updateDoubleTapToSleep(isDoubleTapSbEnabled); + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/shade/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/PanelViewController.java index b4ce95c434fc..1b3b17096285 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/PanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/PanelViewController.java @@ -203,6 +203,7 @@ private void logf(String fmt, Object... args) { protected final SystemClock mSystemClock; protected final ShadeLogger mShadeLog; + protected boolean mIsSbDoubleTapEnabled; protected abstract void onExpandingFinished(); @@ -1354,7 +1355,7 @@ public boolean onTouch(View v, MotionEvent event) { onTrackingStarted(); } if (isFullyCollapsed() && !mHeadsUpManager.hasPinnedHeadsUp() - && !mCentralSurfaces.isBouncerShowing()) { + && !mCentralSurfaces.isBouncerShowing() && !mIsSbDoubleTapEnabled) { startOpening(event); } break; diff --git a/packages/SystemUI/src/com/android/systemui/smartpixels/Grids.java b/packages/SystemUI/src/com/android/systemui/smartpixels/Grids.java new file mode 100644 index 000000000000..e29381c38485 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/smartpixels/Grids.java @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2015, Sergii Pylypenko + * (c) 2018, Joe Maples + * (c) 2018, CarbonROM + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of screen-dimmer-pixel-filter nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +package com.android.systemui.smartpixels; + +public class Grids { + + public static final int GridSize = 64; + public static final int GridSideSize = 8; + + public static String[] PatternNames = new String[] { + "12%", + "25%", + "38%", + "50%", + "62%", + "75%", + "88%", + }; + + public static byte[][] Patterns = new byte[][] { + { + 1, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 0, + 0, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1, 0, 0, + 0, 0, 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1, + 0, 0, 0, 0, 1, 0, 0, 0, + }, + { + 1, 0, 0, 0, 1, 0, 0, 0, + 0, 0, 1, 0, 0, 0, 1, 0, + 0, 1, 0, 0, 0, 1, 0, 0, + 0, 0, 0, 1, 0, 0, 0, 1, + 1, 0, 0, 0, 1, 0, 0, 0, + 0, 0, 1, 0, 0, 0, 1, 0, + 0, 1, 0, 0, 0, 1, 0, 0, + 0, 0, 0, 1, 0, 0, 0, 1, + }, + { + 1, 0, 0, 0, 1, 0, 0, 0, + 0, 1, 0, 1, 0, 1, 0, 1, + 0, 0, 1, 0, 0, 0, 1, 0, + 0, 1, 0, 1, 0, 1, 0, 1, + 1, 0, 0, 0, 1, 0, 0, 0, + 0, 1, 0, 1, 0, 1, 0, 1, + 0, 0, 1, 0, 0, 0, 1, 0, + 0, 1, 0, 1, 0, 1, 0, 1, + }, + { + 1, 0, 1, 0, 1, 0, 1, 0, + 0, 1, 0, 1, 0, 1, 0, 1, + 1, 0, 1, 0, 1, 0, 1, 0, + 0, 1, 0, 1, 0, 1, 0, 1, + 1, 0, 1, 0, 1, 0, 1, 0, + 0, 1, 0, 1, 0, 1, 0, 1, + 1, 0, 1, 0, 1, 0, 1, 0, + 0, 1, 0, 1, 0, 1, 0, 1, + }, + { + 0, 1, 1, 1, 0, 1, 1, 1, + 1, 0, 1, 0, 1, 0, 1, 0, + 1, 1, 0, 1, 1, 1, 0, 1, + 1, 0, 1, 0, 1, 0, 1, 0, + 0, 1, 1, 1, 0, 1, 1, 1, + 1, 0, 1, 0, 1, 0, 1, 0, + 1, 1, 0, 1, 1, 1, 0, 1, + 1, 0, 1, 0, 1, 0, 1, 0, + }, + { + 0, 1, 1, 1, 0, 1, 1, 1, + 1, 1, 0, 1, 1, 1, 0, 1, + 1, 0, 1, 1, 1, 0, 1, 1, + 1, 1, 1, 0, 1, 1, 1, 0, + 0, 1, 1, 1, 0, 1, 1, 1, + 1, 1, 0, 1, 1, 1, 0, 1, + 1, 0, 1, 1, 1, 0, 1, 1, + 1, 1, 1, 0, 1, 1, 1, 0, + }, + { + 0, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 0, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 0, 1, + 1, 0, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 0, 1, 1, + 1, 1, 0, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 0, + 1, 1, 1, 1, 0, 1, 1, 1, + }, + }; + + // Indexes to shift screen pattern in both vertical and horizontal directions + public static byte[] GridShift = new byte[] { + 0, 1, 8, 9, 2, 3, 10, 11, + 4, 5, 12, 13, 6, 7, 14, 15, + 16, 17, 24, 25, 18, 19, 26, 27, + 20, 21, 28, 29, 22, 23, 30, 31, + 32, 33, 40, 41, 34, 35, 42, 43, + 36, 37, 44, 45, 38, 39, 46, 47, + 48, 49, 56, 57, 50, 51, 58, 59, + 52, 53, 60, 61, 54, 55, 62, 63, + }; + + public static int[] ShiftTimeouts = new int[] { // In milliseconds + 15 * 1000, + 30 * 1000, + 60 * 1000, + 2 * 60 * 1000, + 5 * 60 * 1000, + 10 * 60 * 1000, + 20 * 60 * 1000, + 30 * 60 * 1000, + 60 * 60 * 1000, + }; + +} diff --git a/packages/SystemUI/src/com/android/systemui/smartpixels/SmartPixelsReceiver.java b/packages/SystemUI/src/com/android/systemui/smartpixels/SmartPixelsReceiver.java new file mode 100644 index 000000000000..f1cac3c59cd9 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/smartpixels/SmartPixelsReceiver.java @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2018 CarbonROM + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.smartpixels; + +import android.content.BroadcastReceiver; +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.database.ContentObserver; +import android.os.Handler; +import android.os.PowerManager; +import android.os.UserHandle; +import android.provider.Settings; +import android.util.Log; + +public class SmartPixelsReceiver extends BroadcastReceiver { + private static final String TAG = "SmartPixelsReceiver"; + + private Context mContext; + private Handler mHandler; + private ContentResolver mResolver; + private final PowerManager mPowerManager; + private SettingsObserver mSettingsObserver; + private Intent mSmartPixelsService; + private IntentFilter mFilter; + + private boolean mEnabled; + private boolean mOnPowerSave; + private boolean mPowerSave; + private boolean mServiceRunning = false; + private boolean mRegisteredReceiver = false; + + public SmartPixelsReceiver(Context context) { + mContext = context; + mHandler = new Handler(); + mResolver = mContext.getContentResolver(); + mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); + mSmartPixelsService = new Intent(mContext, + com.android.systemui.smartpixels.SmartPixelsService.class); + + mFilter = new IntentFilter(); + mFilter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED); + mFilter.addAction(Intent.ACTION_USER_FOREGROUND); + + initiateSettingsObserver(); + } + + private void registerReceiver() { + mContext.registerReceiver(this, mFilter); + mRegisteredReceiver = true; + } + + private void unregisterReceiver() { + mContext.unregisterReceiver(this); + mRegisteredReceiver = false; + } + + private void initiateSettingsObserver() { + mSettingsObserver = new SettingsObserver(mHandler); + mSettingsObserver.observe(); + mSettingsObserver.update(); + } + + private class SettingsObserver extends ContentObserver { + SettingsObserver(Handler handler) { + super(handler); + } + + void observe() { + mResolver.registerContentObserver(Settings.System.getUriFor( + Settings.System.SMART_PIXELS_ENABLE), + false, this, UserHandle.USER_ALL); + mResolver.registerContentObserver(Settings.System.getUriFor( + Settings.System.SMART_PIXELS_ON_POWER_SAVE), + false, this, UserHandle.USER_ALL); + mResolver.registerContentObserver(Settings.System.getUriFor( + Settings.System.SMART_PIXELS_PATTERN), + false, this, UserHandle.USER_ALL); + mResolver.registerContentObserver(Settings.System.getUriFor( + Settings.System.SMART_PIXELS_SHIFT_TIMEOUT), + false, this, UserHandle.USER_ALL); + update(); + } + + @Override + public void onChange(boolean selfChange) { + update(); + } + + public void update() { + mEnabled = (Settings.System.getIntForUser( + mResolver, Settings.System.SMART_PIXELS_ENABLE, + 0, UserHandle.USER_CURRENT) == 1); + mOnPowerSave = (Settings.System.getIntForUser( + mResolver, Settings.System.SMART_PIXELS_ON_POWER_SAVE, + 0, UserHandle.USER_CURRENT) == 1); + mPowerSave = mPowerManager.isPowerSaveMode(); + + if (mEnabled || mOnPowerSave) { + if (!mRegisteredReceiver) + registerReceiver(); + } else if (mRegisteredReceiver) { + unregisterReceiver(); + } + + if (!mEnabled && mOnPowerSave) { + if (mPowerSave && !mServiceRunning) { + mContext.startService(mSmartPixelsService); + mServiceRunning = true; + Log.d(TAG, "Started Smart Pixels Service by Power Save enable"); + } else if (!mPowerSave && mServiceRunning) { + mContext.stopService(mSmartPixelsService); + mServiceRunning = false; + Log.d(TAG, "Stopped Smart Pixels Service by Power Save disable"); + } else if (mPowerSave && mServiceRunning) { + mContext.stopService(mSmartPixelsService); + mContext.startService(mSmartPixelsService); + Log.d(TAG, "Restarted Smart Pixels Service by Power Save enable"); + } + } else if (mEnabled && !mServiceRunning) { + mContext.startService(mSmartPixelsService); + mServiceRunning = true; + Log.d(TAG, "Started Smart Pixels Service by enable"); + } else if (!mEnabled && mServiceRunning) { + mContext.stopService(mSmartPixelsService); + mServiceRunning = false; + Log.d(TAG, "Stopped Smart Pixels Service by disable"); + } else if (mEnabled && mServiceRunning) { + mContext.stopService(mSmartPixelsService); + mContext.startService(mSmartPixelsService); + Log.d(TAG, "Restarted Smart Pixels Service"); + } + } + } + + @Override + public void onReceive(final Context context, Intent intent) { + mSettingsObserver.update(); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/smartpixels/SmartPixelsService.java b/packages/SystemUI/src/com/android/systemui/smartpixels/SmartPixelsService.java new file mode 100644 index 000000000000..b8cd8bb19909 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/smartpixels/SmartPixelsService.java @@ -0,0 +1,244 @@ +/* + * Copyright (c) 2015, Sergii Pylypenko + * (c) 2018, Joe Maples + * (c) 2018, Adin Kwok + * (c) 2018, CarbonROM + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of screen-dimmer-pixel-filter nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +package com.android.systemui.smartpixels; + +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY; + +import android.Manifest; +import android.app.Service; +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.database.ContentObserver; +import android.graphics.Bitmap; +import android.graphics.Color; +import android.graphics.PixelFormat; +import android.graphics.Point; +import android.graphics.Shader; +import android.graphics.drawable.BitmapDrawable; +import android.os.Handler; +import android.os.IBinder; +import android.os.PowerManager; +import android.os.UserHandle; +import android.os.UserManager; +import android.provider.Settings; +import android.util.DisplayMetrics; +import android.util.Log; +import android.view.View; +import android.view.WindowManager; +import android.widget.ImageView; + +import com.android.systemui.R; + +public class SmartPixelsService extends Service { + public static final String LOG = "SmartPixelsService"; + + private WindowManager windowManager; + private ImageView view = null; + private Bitmap bmp; + + private boolean destroyed = false; + public static boolean running = false; + + private int startCounter = 0; + private Context mContext; + + // Pixel Filter Settings + private int mPattern = 3; + private int mShiftTimeout = 4; + + @Override + public IBinder onBind(Intent intent) { + return null; + } + + @Override + public void onCreate() { + super.onCreate(); + running = true; + mContext = this; + updateSettings(); + Log.d(LOG, "Service started"); + startFilter(); + } + + public void startFilter() { + if (view != null) { + return; + } + + windowManager = (WindowManager) getSystemService(WINDOW_SERVICE); + + view = new ImageView(this); + DisplayMetrics metrics = new DisplayMetrics(); + windowManager.getDefaultDisplay().getRealMetrics(metrics); + bmp = Bitmap.createBitmap(Grids.GridSideSize, Grids.GridSideSize, Bitmap.Config.ARGB_4444); + + updatePattern(); + BitmapDrawable draw = new BitmapDrawable(bmp); + draw.setTileModeXY(Shader.TileMode.REPEAT, Shader.TileMode.REPEAT); + draw.setFilterBitmap(false); + draw.setAntiAlias(false); + draw.setTargetDensity(metrics.densityDpi); + + view.setBackground(draw); + + WindowManager.LayoutParams params = getLayoutParams(); + params.privateFlags |= PRIVATE_FLAG_TRUSTED_OVERLAY; + try { + windowManager.addView(view, params); + } catch (Exception e) { + running = false; + view = null; + return; + } + + startCounter++; + final int handlerStartCounter = startCounter; + final Handler handler = new Handler(); + final PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); + handler.postDelayed(new Runnable() { + @Override + public void run() { + if (view == null || destroyed || handlerStartCounter != startCounter) { + return; + } else if (pm.isInteractive()) { + updatePattern(); + view.invalidate(); + } + if (!destroyed) { + handler.postDelayed(this, Grids.ShiftTimeouts[mShiftTimeout]); + } + } + }, Grids.ShiftTimeouts[mShiftTimeout]); + } + + public void stopFilter() { + if (view == null) { + return; + } + + startCounter++; + + windowManager.removeView(view); + view = null; + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + return START_STICKY; + } + + @Override + public void onDestroy() { + super.onDestroy(); + destroyed = true; + stopFilter(); + Log.d(LOG, "Service stopped"); + running = false; + } + + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + Log.d(LOG, "Screen orientation changed, updating window layout"); + WindowManager.LayoutParams params = getLayoutParams(); + windowManager.updateViewLayout(view, params); + } + + private WindowManager.LayoutParams getLayoutParams() + { + Point displaySize = new Point(); + windowManager.getDefaultDisplay().getRealSize(displaySize); + Point windowSize = new Point(); + windowManager.getDefaultDisplay().getRealSize(windowSize); + Resources res = getResources(); + int mStatusBarHeight = res.getDimensionPixelOffset(R.dimen.status_bar_height); + displaySize.x += displaySize.x - windowSize.x + (mStatusBarHeight * 2); + displaySize.y += displaySize.y - windowSize.y + (mStatusBarHeight * 2); + + WindowManager.LayoutParams params = new WindowManager.LayoutParams( + displaySize.x, + displaySize.y, + 0, + 0, + WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY, + WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | + WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE | + WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED | + WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN | + WindowManager.LayoutParams.FLAG_LAYOUT_IN_OVERSCAN | + WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS | + WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED, + PixelFormat.TRANSPARENT + ); + + // Use the rounded corners overlay to hide it from screenshots. See 132c9f514. + params.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY; + + params.dimAmount = WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_NONE; + params.systemUiVisibility = View.SYSTEM_UI_FLAG_LOW_PROFILE; + return params; + } + + private int getShift() { + long shift = (System.currentTimeMillis() / Grids.ShiftTimeouts[mShiftTimeout]) % Grids.GridSize; + return Grids.GridShift[(int)shift]; + } + + private void updatePattern() { + int shift = getShift(); + int shiftX = shift % Grids.GridSideSize; + int shiftY = shift / Grids.GridSideSize; + for (int i = 0; i < Grids.GridSize; i++) { + int x = (i + shiftX) % Grids.GridSideSize; + int y = ((i / Grids.GridSideSize) + shiftY) % Grids.GridSideSize; + int color = (Grids.Patterns[mPattern][i] == 0) ? Color.TRANSPARENT : Color.BLACK; + bmp.setPixel(x, y, color); + } + } + + private void updateSettings() { + mPattern = Settings.System.getIntForUser( + mContext.getContentResolver(), Settings.System.SMART_PIXELS_PATTERN, + 3, UserHandle.USER_CURRENT); + mShiftTimeout = Settings.System.getIntForUser( + mContext.getContentResolver(), Settings.System.SMART_PIXELS_SHIFT_TIMEOUT, + 4, UserHandle.USER_CURRENT); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt b/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt index fbb51aef06f0..e615a8d122c1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt @@ -109,7 +109,7 @@ open class BlurUtils @Inject constructor( * @return {@code true} when supported. */ open fun supportsBlursOnWindows(): Boolean { - return CROSS_WINDOW_BLUR_SUPPORTED && ActivityManager.isHighEndGfx() && + return CROSS_WINDOW_BLUR_SUPPORTED && !ActivityManager.isLowRamDeviceStatic() && crossWindowBlurListeners.isCrossWindowBlurEnabled() && !SystemProperties.getBoolean("persist.sysui.disableBlur", false) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java index 04621168493b..9ebfdc127fbe 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java @@ -162,6 +162,7 @@ public class CommandQueue extends IStatusBar.Stub implements private static final int MSG_REGISTER_NEARBY_MEDIA_DEVICE_PROVIDER = 66 << MSG_SHIFT; private static final int MSG_UNREGISTER_NEARBY_MEDIA_DEVICE_PROVIDER = 67 << MSG_SHIFT; private static final int MSG_TILE_SERVICE_REQUEST_LISTENING_STATE = 68 << MSG_SHIFT; + private static final int MSG_TOGGLE_CAMERA_FLASH = 69 << MSG_SHIFT; public static final int FLAG_EXCLUDE_NONE = 0; public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0; @@ -296,7 +297,7 @@ default void handleSystemKey(int arg1) { } default void showPinningEnterExitToast(boolean entering) { } default void showPinningEscapeToast() { } default void handleShowGlobalActionsMenu() { } - default void handleShowShutdownUi(boolean isReboot, String reason) { } + default void handleShowShutdownUi(boolean isReboot, String reason, boolean rebootCustom) { } default void showWirelessChargingAnimation(int batteryLevel) { } @@ -470,6 +471,8 @@ default void registerNearbyMediaDevicesProvider( */ default void unregisterNearbyMediaDevicesProvider( @NonNull INearbyMediaDevicesProvider provider) {} + + default void toggleCameraFlash() { } } public CommandQueue(Context context) { @@ -889,10 +892,10 @@ public void setTopAppHidesStatusBar(boolean hidesStatusBar) { } @Override - public void showShutdownUi(boolean isReboot, String reason) { + public void showShutdownUi(boolean isReboot, String reason, boolean rebootCustom) { synchronized (mLock) { mHandler.removeMessages(MSG_SHOW_SHUTDOWN_UI); - mHandler.obtainMessage(MSG_SHOW_SHUTDOWN_UI, isReboot ? 1 : 0, 0, reason) + mHandler.obtainMessage(MSG_SHOW_SHUTDOWN_UI, isReboot ? 1 : 0, rebootCustom ? 1 : 0, reason) .sendToTarget(); } } @@ -1250,6 +1253,14 @@ public void unregisterNearbyMediaDevicesProvider( .sendToTarget(); } + @Override + public void toggleCameraFlash() { + synchronized (mLock) { + mHandler.removeMessages(MSG_TOGGLE_CAMERA_FLASH); + mHandler.sendEmptyMessage(MSG_TOGGLE_CAMERA_FLASH); + } + } + private final class H extends Handler { private H(Looper l) { super(l); @@ -1432,7 +1443,7 @@ public void handleMessage(Message msg) { break; case MSG_SHOW_SHUTDOWN_UI: for (int i = 0; i < mCallbacks.size(); i++) { - mCallbacks.get(i).handleShowShutdownUi(msg.arg1 != 0, (String) msg.obj); + mCallbacks.get(i).handleShowShutdownUi(msg.arg1 != 0, (String) msg.obj, msg.arg2 != 0); } break; case MSG_SET_TOP_APP_HIDES_STATUS_BAR: @@ -1685,6 +1696,11 @@ public void handleMessage(Message msg) { mCallbacks.get(i).requestTileServiceListeningState(component); } break; + case MSG_TOGGLE_CAMERA_FLASH: + for (int i = 0; i < mCallbacks.size(); i++) { + mCallbacks.get(i).toggleCameraFlash(); + } + break; } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java index 073ab8b16864..da86723a9794 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java @@ -40,6 +40,7 @@ import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_USER_LOCKED; import static com.android.systemui.keyguard.ScreenLifecycle.SCREEN_ON; import static com.android.systemui.plugins.FalsingManager.LOW_PENALTY; +import static com.android.keyguard.KeyguardUpdateMonitor.FACE_UNLOCK_BEHAVIOR_SWIPE; import android.app.admin.DevicePolicyManager; import android.content.BroadcastReceiver; @@ -62,6 +63,7 @@ import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; +import android.provider.Settings; import android.text.TextUtils; import android.text.format.Formatter; import android.util.Log; @@ -129,6 +131,8 @@ public class KeyguardIndicationController { private static final int MSG_SHOW_ACTION_TO_UNLOCK = 2; private static final int MSG_HIDE_BIOMETRIC_MESSAGE = 3; private static final int MSG_RESET_ERROR_MESSAGE_ON_SCREEN_ON = 4; + private static final int MSG_SHOW_RECOGNIZING_FACE = 5; + private static final int MSG_HIDE_RECOGNIZING_FACE = 6; private static final long TRANSIENT_BIOMETRIC_ERROR_TIMEOUT = 1300; public static final long DEFAULT_HIDE_DELAY_MS = 3500 + KeyguardIndicationTextView.Y_IN_DURATION; @@ -176,14 +180,18 @@ public class KeyguardIndicationController { private boolean mBatteryOverheated; private boolean mEnableBatteryDefender; private int mChargingSpeed; - private int mChargingWattage; + private double mChargingWattage; private int mBatteryLevel; private boolean mBatteryPresent = true; private long mChargingTimeRemaining; + private int mChargingCurrent; + private double mChargingVoltage; + private float mTemperature; private String mBiometricErrorMessageToShowOnScreenOn; private final Set mCoExFaceAcquisitionMsgIdsToShow; private final FaceHelpMessageDeferral mFaceAcquiredMessageDeferral; private boolean mInited; + private boolean mFaceDetectionRunning; private KeyguardUpdateMonitorCallback mUpdateMonitorCallback; @@ -208,6 +216,15 @@ public void onScreenTurnedOn() { mBiometricErrorMessageToShowOnScreenOn = null; } } + + @Override + public void onScreenTurnedOff() { + if (mFaceDetectionRunning) { + mFaceDetectionRunning = false; + mBiometricErrorMessageToShowOnScreenOn = null; + hideFaceUnlockRecognizingMessage(); + } + } }; /** @@ -273,6 +290,11 @@ public void handleMessage(Message msg) { hideBiometricMessage(); } else if (msg.what == MSG_RESET_ERROR_MESSAGE_ON_SCREEN_ON) { mBiometricErrorMessageToShowOnScreenOn = null; + } else if (msg.what == MSG_SHOW_RECOGNIZING_FACE){ + mBiometricErrorMessageToShowOnScreenOn = null; + showFaceUnlockRecognizingMessage(); + } else if (msg.what == MSG_HIDE_RECOGNIZING_FACE){ + hideFaceUnlockRecognizingMessage(); } } }; @@ -792,6 +814,47 @@ private void hideBiometricMessage() { } } + private void showFaceUnlockRecognizingMessage() { + if (mKeyguardUpdateMonitor.getFaceUnlockBehavior() == FACE_UNLOCK_BEHAVIOR_SWIPE){ + return; + } + + String faceUnlockMessage = mContext.getResources().getString( + R.string.face_unlock_recognizing); + mBiometricMessage = faceUnlockMessage; + + mHandler.removeMessages(MSG_SHOW_ACTION_TO_UNLOCK); + mHandler.removeMessages(MSG_HIDE_BIOMETRIC_MESSAGE); + + mRotateTextViewController.updateIndication( + INDICATION_TYPE_BIOMETRIC_MESSAGE, + new KeyguardIndication.Builder() + .setMessage(mBiometricMessage) + .setMinVisibilityMillis(6000L) // 6 seconds + .setTextColor(mInitialTextColorState) + .build(), + true + ); + + if (mDozing) { + updateDeviceEntryIndication(false); + } + } + + private void hideFaceUnlockRecognizingMessage() { + if (mKeyguardUpdateMonitor.getFaceUnlockBehavior() == FACE_UNLOCK_BEHAVIOR_SWIPE){ + return; + } + + String faceUnlockMessage = mContext.getResources().getString( + R.string.face_unlock_recognizing); + if (mBiometricMessage != null && mBiometricMessage == faceUnlockMessage) { + mBiometricMessage = null; + mHandler.removeMessages(MSG_HIDE_BIOMETRIC_MESSAGE); + updateBiometricMessage(); + } + } + /** * Hides transient indication. */ @@ -847,7 +910,7 @@ protected final void updateDeviceEntryIndication(boolean animate) { if (!TextUtils.equals(mTopIndicationView.getText(), newIndication)) { mWakeLock.setAcquired(true); mTopIndicationView.switchIndication(newIndication, null, - true, () -> mWakeLock.setAcquired(false)); + animate, () -> mWakeLock.setAcquired(false)); } return; } @@ -861,14 +924,17 @@ protected final void updateDeviceEntryIndication(boolean animate) { protected String computePowerIndication() { int chargingId; + String percentage = NumberFormat.getPercentInstance().format(mBatteryLevel / 100f); if (mBatteryOverheated) { chargingId = R.string.keyguard_plugged_in_charging_limited; - String percentage = NumberFormat.getPercentInstance().format(mBatteryLevel / 100f); return mContext.getResources().getString(chargingId, percentage); } else if (mPowerCharged) { return mContext.getResources().getString(R.string.keyguard_charged); } + final boolean hasSuperDartCharger = mContext.getResources().getBoolean( + com.android.internal.R.bool.config_hasSuperDartCharger); + final boolean hasChargingTime = mChargingTimeRemaining > 0; if (mPowerPluggedInWired) { switch (mChargingSpeed) { @@ -877,6 +943,25 @@ protected String computePowerIndication() { ? R.string.keyguard_indication_charging_time_fast : R.string.keyguard_plugged_in_charging_fast; break; + case BatteryStatus.CHARGING_DASH: + chargingId = hasChargingTime + ? R.string.keyguard_indication_dash_charging_time + : R.string.keyguard_plugged_in_dash_charging; + break; + case BatteryStatus.CHARGING_WARP: + chargingId = hasChargingTime + ? R.string.keyguard_indication_warp_charging_time + : R.string.keyguard_plugged_in_warp_charging; + break; + case BatteryStatus.CHARGING_VOOC: + chargingId = hasChargingTime + ? (hasSuperDartCharger + ? R.string.keyguard_indication_superdart_charging_time + : R.string.keyguard_indication_vooc_charging_time) + : (hasSuperDartCharger + ? R.string.keyguard_plugged_in_superdart_charging + : R.string.keyguard_plugged_in_vooc_charging); + break; case BatteryStatus.CHARGING_SLOWLY: chargingId = hasChargingTime ? R.string.keyguard_indication_charging_time_slowly @@ -902,14 +987,44 @@ protected String computePowerIndication() { : R.string.keyguard_plugged_in; } - String percentage = NumberFormat.getPercentInstance().format(mBatteryLevel / 100f); + String batteryInfo = ""; + int current = 0; + double voltage = 0; + boolean showbatteryInfo = Settings.System.getIntForUser(mContext.getContentResolver(), + Settings.System.LOCKSCREEN_BATTERY_INFO, 1, UserHandle.USER_CURRENT) == 1; + if (showbatteryInfo) { + if (mChargingCurrent > 0) { + current = (mChargingCurrent < 5 ? (mChargingCurrent * 1000) + : (mChargingCurrent < 4000 ? mChargingCurrent : (mChargingCurrent / 1000))); + batteryInfo = batteryInfo + current + "mA"; + } + if (mChargingVoltage > 0 && mChargingCurrent > 0) { + voltage = mChargingVoltage / 1000 / 1000; + batteryInfo = (batteryInfo == "" ? "" : batteryInfo + " · ") + + String.format("%.1f" , ((double) current / 1000) * voltage) + "W"; + } + if (mChargingVoltage > 0) { + batteryInfo = (batteryInfo == "" ? "" : batteryInfo + " · ") + + String.format("%.1f" , voltage) + "V"; + } + if (mTemperature > 0) { + batteryInfo = (batteryInfo == "" ? "" : batteryInfo + " · ") + + mTemperature / 10 + "°C"; + } + if (batteryInfo != "") { + batteryInfo = "\n" + batteryInfo; + } + } + if (hasChargingTime) { String chargingTimeFormatted = Formatter.formatShortElapsedTimeRoundingUpToMinutes( mContext, mChargingTimeRemaining); - return mContext.getResources().getString(chargingId, chargingTimeFormatted, + String chargingText = mContext.getResources().getString(chargingId, chargingTimeFormatted, percentage); + return chargingText + batteryInfo; } else { - return mContext.getResources().getString(chargingId, percentage); + String chargingText = mContext.getResources().getString(chargingId, percentage); + return chargingText + batteryInfo; } } @@ -1018,8 +1133,11 @@ public void onRefreshBatteryInfo(BatteryStatus status) { mPowerPluggedInDock = status.isPluggedInDock() && isChargingOrFull; mPowerPluggedIn = status.isPluggedIn() && isChargingOrFull; mPowerCharged = status.isCharged(); + mChargingCurrent = status.maxChargingCurrent; + mChargingVoltage = status.maxChargingVoltage; mChargingWattage = status.maxChargingWattage; mChargingSpeed = status.getChargingSpeed(mContext); + mTemperature = status.temperature; mBatteryLevel = status.level; mBatteryPresent = status.present; mBatteryOverheated = status.isOverheated(); @@ -1237,11 +1355,17 @@ public void onTrustAgentErrorMessage(CharSequence message) { @Override public void onBiometricRunningStateChanged(boolean running, BiometricSourceType biometricSourceType) { - if (running && biometricSourceType == FACE) { - // Let's hide any previous messages when authentication starts, otherwise - // multiple auth attempts would overlap. - hideBiometricMessage(); - mBiometricErrorMessageToShowOnScreenOn = null; + if (biometricSourceType == FACE) { + mFaceDetectionRunning = running; + if (running) { + mHandler.removeMessages(MSG_HIDE_RECOGNIZING_FACE); + mHandler.removeMessages(MSG_SHOW_RECOGNIZING_FACE); + mHandler.sendEmptyMessageDelayed(MSG_SHOW_RECOGNIZING_FACE, 100); + } else { + mHandler.removeMessages(MSG_SHOW_RECOGNIZING_FACE); + mHandler.removeMessages(MSG_HIDE_RECOGNIZING_FACE); + mHandler.sendEmptyMessageDelayed(MSG_HIDE_RECOGNIZING_FACE, 100); + } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt index 80069319601f..16960b9a023a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt @@ -13,6 +13,7 @@ import android.view.View import android.view.ViewConfiguration import androidx.annotation.FloatRange import androidx.annotation.VisibleForTesting +import com.android.internal.util.bootleg.BootlegUtils import com.android.systemui.Dumpable import com.android.systemui.ExpandHelper import com.android.systemui.Gefingerpoken @@ -736,6 +737,13 @@ class DragDownHelper( private var draggedFarEnough = false private var startingChild: ExpandableView? = null private var lastHeight = 0f + + private var doubleTapToSleepEnabled = false + private var statusBarHeaderHeight = 0 + private var lastDownEvent = -1 + private var doubleTapTimeout = -1 + private var goToSleep: Runnable? = null + var isDraggingDown = false private set @@ -761,6 +769,12 @@ class DragDownHelper( val configuration = ViewConfiguration.get(context) touchSlop = configuration.scaledTouchSlop.toFloat() slopMultiplier = configuration.scaledAmbiguousGestureMultiplier + doubleTapTimeout = ViewConfiguration.getDoubleTapTimeout() + statusBarHeaderHeight = context + .resources.getDimensionPixelSize(R.dimen.status_bar_header_height_keyguard) + goToSleep = Runnable { + BootlegUtils.switchScreenOff(context) + } } override fun onInterceptTouchEvent(event: MotionEvent): Boolean { @@ -773,6 +787,19 @@ class DragDownHelper( startingChild = null initialTouchY = y initialTouchX = x + if (doubleTapToSleepEnabled && y < statusBarHeaderHeight) { + val eventTime = event.getEventTime().toInt() + if (lastDownEvent != -1) { + val diff = eventTime - lastDownEvent + + if (diff < doubleTapTimeout) { + goToSleep?.run() + } + lastDownEvent = -1 + } else { + lastDownEvent = eventTime + } + } } MotionEvent.ACTION_MOVE -> { val h = y - initialTouchY @@ -915,4 +942,8 @@ class DragDownHelper( host.getLocationOnScreen(temp2) return expandCallback.getChildAtRawPosition(x + temp2[0], y + temp2[1]) } + + public fun updateDoubleTapToSleep(updateDoubleTapToSleep: Boolean) { + doubleTapToSleepEnabled = updateDoubleTapToSleep + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java index 8699441da726..4ff479e28891 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java @@ -59,6 +59,8 @@ import com.android.systemui.util.ListenerSet; import com.android.systemui.util.settings.SecureSettings; +import ink.kaleidoscope.ParallelSpaceManager; + import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; @@ -153,6 +155,7 @@ public void onReceive(Context context, Intent intent) { case Intent.ACTION_USER_ADDED: case Intent.ACTION_MANAGED_PROFILE_AVAILABLE: case Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE: + case Intent.ACTION_PARALLEL_SPACE_CHANGED: updateCurrentProfilesCache(); break; case Intent.ACTION_USER_UNLOCKED: @@ -292,6 +295,7 @@ public void onChange(boolean selfChange) { filter.addAction(Intent.ACTION_USER_UNLOCKED); filter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE); filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE); + filter.addAction(Intent.ACTION_PARALLEL_SPACE_CHANGED); mBroadcastDispatcher.registerReceiver(mBaseBroadcastReceiver, filter, null /* executor */, UserHandle.ALL); @@ -484,7 +488,9 @@ private void updateCurrentProfilesCache() { mCurrentProfiles.clear(); mCurrentManagedProfiles.clear(); if (mUserManager != null) { - for (UserInfo user : mUserManager.getProfiles(mCurrentUserId)) { + List users = mUserManager.getProfiles(mCurrentUserId); + users.addAll(ParallelSpaceManager.getInstance().getParallelUsers()); + for (UserInfo user : users) { mCurrentProfiles.put(user.id, user); if (UserManager.USER_TYPE_PROFILE_MANAGED.equals(user.userType)) { mCurrentManagedProfiles.put(user.id, user); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt index cb13fcf246cb..d6cec249c35f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt @@ -75,7 +75,7 @@ class NotificationShadeDepthController @Inject constructor( private const val VELOCITY_SCALE = 100f private const val MAX_VELOCITY = 3000f private const val MIN_VELOCITY = -MAX_VELOCITY - private const val INTERACTION_BLUR_FRACTION = 0.8f + private const val INTERACTION_BLUR_FRACTION = 0.9f private const val ANIMATION_BLUR_FRACTION = 1f - INTERACTION_BLUR_FRACTION private const val TAG = "DepthController" } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputNotificationRebuilder.java b/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputNotificationRebuilder.java index 90abec17771c..71d93784e096 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputNotificationRebuilder.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputNotificationRebuilder.java @@ -134,7 +134,8 @@ StatusBarNotification rebuildWithRemoteInputInserted(NotificationEntry entry, newNotification, sbn.getUser(), sbn.getOverrideGroupKey(), - sbn.getPostTime()); + sbn.getPostTime(), + sbn.getIsContentSecure()); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarImsView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarImsView.java new file mode 100644 index 000000000000..5813ab402b6a --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarImsView.java @@ -0,0 +1,218 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar; + +import android.content.Context; +import android.content.res.ColorStateList; +import android.graphics.Rect; +import android.os.UserHandle; +import android.provider.Settings; +import android.util.AttributeSet; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.LinearLayout; + +import com.android.systemui.R; +import com.android.systemui.statusbar.connectivity.ImsIconState; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import static com.android.systemui.plugins.DarkIconDispatcher.getTint; +import static com.android.systemui.statusbar.StatusBarIconView.STATE_DOT; +import static com.android.systemui.statusbar.StatusBarIconView.STATE_HIDDEN; +import static com.android.systemui.statusbar.StatusBarIconView.STATE_ICON; + +public class StatusBarImsView extends FrameLayout implements + StatusIconDisplayable { + private static final String TAG = "StatusBarImsView"; + + /// Used to show etc dots + private StatusBarIconView mDotView; + + private ImsIconState mState; + private LinearLayout mImsGroup; + private ImageView mVowifiIcon; + private ImageView mVolteIcon; + private String mSlot; + private int mVisibleState = -1; + + public StatusBarImsView(Context context) { + super(context); + } + + public StatusBarImsView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public StatusBarImsView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + public StatusBarImsView(Context context, AttributeSet attrs, int defStyleAttr, + int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + public static StatusBarImsView fromContext(Context context, String slot) { + LayoutInflater inflater = LayoutInflater.from(context); + StatusBarImsView v = (StatusBarImsView) inflater.inflate(R.layout.status_bar_ims_group, null); + v.setSlot(slot); + v.init(); + v.setVisibleState(STATE_ICON); + return v; + } + + public void init() { + mImsGroup = findViewById(R.id.ims_group); + mVowifiIcon = findViewById(R.id.vowifi_icon); + mVolteIcon = findViewById(R.id.volte_icon); + + initDotView(); + } + + private void initDotView() { + mDotView = new StatusBarIconView(mContext, mSlot, null); + mDotView.setVisibleState(STATE_DOT); + + int width = mContext.getResources().getDimensionPixelSize(R.dimen.status_bar_icon_size); + LayoutParams lp = new LayoutParams(width, width); + lp.gravity = Gravity.CENTER_VERTICAL | Gravity.START; + addView(mDotView, lp); + } + + @Override + public String getSlot() { + return mSlot; + } + + public void setSlot(String slot) { + mSlot = slot; + } + + @Override + public void setVisibleState(int state, boolean animate) { + if (state == mVisibleState) { + return; + } + mVisibleState = state; + + switch (state) { + case STATE_ICON: + mImsGroup.setVisibility(View.VISIBLE); + mDotView.setVisibility(View.GONE); + break; + case STATE_DOT: + mImsGroup.setVisibility(View.GONE); + mDotView.setVisibility(View.VISIBLE); + break; + case STATE_HIDDEN: + default: + mImsGroup.setVisibility(View.GONE); + mDotView.setVisibility(View.GONE); + break; + } + } + + @Override + public int getVisibleState() { + return mVisibleState; + } + + @Override + public boolean isIconVisible() { + return mState != null && mState.visible; + } + + public void applyImsState(ImsIconState state) { + boolean requestLayout = false; + + if (state == null) { + requestLayout = getVisibility() != View.GONE; + setVisibility(View.GONE); + mState = null; + } else if (mState == null) { + requestLayout = true; + mState = state; + initViewState(state); + } else if (!mState.equals(state)) { + requestLayout = updateState(state); + } + + if (requestLayout) { + requestLayout(); + } + } + + private boolean updateState(ImsIconState state) { + boolean needsLayout = false; + + if (mState.visible != state.visible + || mState.vowifiVisible != state.vowifiVisible + || mState.volteVisible != state.volteVisible) { + initViewState(state); + needsLayout = true; + } + + mState = state; + return needsLayout; + } + + private void initViewState(ImsIconState state) { + setContentDescription(state.contentDescription); + if (state.vowifiVisible) { + mVolteIcon.setVisibility(View.GONE); + mVowifiIcon.setImageDrawable(mContext.getDrawable(state.vowifiIcon)); + mVowifiIcon.setVisibility(View.VISIBLE); + } else if (state.volteVisible) { + mVowifiIcon.setVisibility(View.GONE); + mVolteIcon.setImageDrawable(mContext.getDrawable(state.volteIcon)); + mVolteIcon.setVisibility(View.VISIBLE); + } else { + mVolteIcon.setVisibility(View.GONE); + mVowifiIcon.setVisibility(View.GONE); + } + setVisibility(state.visible ? View.VISIBLE : View.GONE); + } + + @Override + public void setDecorColor(int color) { + mDotView.setDecorColor(color); + } + + @Override + public void setStaticDrawableColor(int color) { + ColorStateList list = ColorStateList.valueOf(color); + mVolteIcon.setImageTintList(list); + mVowifiIcon.setImageTintList(list); + mDotView.setDecorColor(color); + } + + @Override + public void onDarkChanged(ArrayList areas, float darkIntensity, int tint) { + int areaTint = getTint(areas, this, tint); + ColorStateList color = ColorStateList.valueOf(areaTint); + mVolteIcon.setImageTintList(color); + mVowifiIcon.setImageTintList(color); + mDotView.setDecorColor(areaTint); + mDotView.setIconColor(areaTint, false); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java index 48c6e273bbb4..c700371ce4b4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java @@ -29,6 +29,7 @@ import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; +import android.view.ViewGroup; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.LinearLayout; @@ -58,11 +59,14 @@ public class StatusBarMobileView extends FrameLayout implements DarkReceiver, private ImageView mIn; private ImageView mOut; private ImageView mMobile, mMobileType, mMobileRoaming; + private View mMobileSignalType; private View mMobileRoamingSpace; @StatusBarIconView.VisibleState private int mVisibleState = STATE_HIDDEN; private DualToneHandler mDualToneHandler; private boolean mForceHidden; + private boolean mOldStyleType; + private ImageView mMobileTypeSmall; /** * Designated constructor @@ -117,6 +121,12 @@ public void getDrawingRect(Rect outRect) { outRect.bottom += translationY; } + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + mMobileGroup.measure(widthMeasureSpec, heightMeasureSpec); + setMeasuredDimension(mMobileGroup.getMeasuredWidth(), mMobileGroup.getMeasuredHeight()); + } + private void init() { mDualToneHandler = new DualToneHandler(getContext()); mMobileGroup = findViewById(R.id.mobile_group); @@ -127,6 +137,8 @@ private void init() { mIn = findViewById(R.id.mobile_in); mOut = findViewById(R.id.mobile_out); mInoutContainer = findViewById(R.id.inout_container); + mMobileSignalType = findViewById(R.id.mobile_signal_type); + mMobileTypeSmall = findViewById(R.id.mobile_type_small); mMobileDrawable = new SignalDrawable(getContext()); mMobile.setImageDrawable(mMobileDrawable); @@ -176,17 +188,50 @@ private void initViewState() { mMobileType.setImageResource(mState.typeId); mMobileType.setVisibility(View.VISIBLE); } else { - mMobileType.setVisibility(View.GONE); + mMobile.setVisibility(View.GONE); + } + + boolean showRoamingSpace = false; + if (mState.typeId > 0) { + if (mOldStyleType) { + showOldStyle(mState); + showRoamingSpace = true; + } else { + showNewStyle(mState); + } + } else { + hideIndicators(); + } + if (mState.roaming) { + mMobileTypeSmall.setVisibility(View.GONE); + setMobileSignalWidth(true); } mMobile.setVisibility(mState.showTriangle ? View.VISIBLE : View.GONE); mMobileRoaming.setVisibility(mState.roaming ? View.VISIBLE : View.GONE); - mMobileRoamingSpace.setVisibility(mState.roaming ? View.VISIBLE : View.GONE); + mMobileRoamingSpace.setVisibility(mState.roaming || showRoamingSpace ? View.VISIBLE : View.GONE); mIn.setVisibility(mState.activityIn ? View.VISIBLE : View.GONE); mOut.setVisibility(mState.activityOut ? View.VISIBLE : View.GONE); mInoutContainer.setVisibility((mState.activityIn || mState.activityOut) ? View.VISIBLE : View.GONE); } + private void setMobileSignalWidth(boolean small) { + if (!small) { + final int paddingLimit = mContext.getResources().getDimensionPixelSize( + R.dimen.status_bar_mobile_type_padding_limit); + final int padding = mMobileTypeSmall.getWidth() < paddingLimit + ? mContext.getResources().getDimensionPixelSize( + R.dimen.status_bar_mobile_type_padding) + : 0; + mMobileTypeSmall.setPadding(padding, 0, 0, 0); + } + final ViewGroup.LayoutParams p = mMobileSignalType.getLayoutParams(); + p.width = mContext.getResources().getDimensionPixelSize(small + ? R.dimen.status_bar_mobile_signal_width + : R.dimen.status_bar_mobile_signal_with_type_width); + mMobileSignalType.setLayoutParams(p); + } + private boolean updateState(MobileIconState state) { boolean needsLayout = false; @@ -199,20 +244,28 @@ private boolean updateState(MobileIconState state) { if (mState.strengthId != state.strengthId) { mMobileDrawable.setLevel(state.strengthId); } + boolean showRoamingSpace = false; if (mState.typeId != state.typeId) { needsLayout |= state.typeId == 0 || mState.typeId == 0; if (state.typeId != 0) { - mMobileType.setContentDescription(state.typeContentDescription); - mMobileType.setImageResource(state.typeId); - mMobileType.setVisibility(View.VISIBLE); + if (mOldStyleType) { + showOldStyle(state); + showRoamingSpace = true; + } else { + showNewStyle(state); + } } else { - mMobileType.setVisibility(View.GONE); + hideIndicators(); } } mMobile.setVisibility(state.showTriangle ? View.VISIBLE : View.GONE); + if (state.roaming) { + mMobileTypeSmall.setVisibility(View.GONE); + setMobileSignalWidth(true); + } mMobileRoaming.setVisibility(state.roaming ? View.VISIBLE : View.GONE); - mMobileRoamingSpace.setVisibility(state.roaming ? View.VISIBLE : View.GONE); + mMobileRoamingSpace.setVisibility(showRoamingSpace || state.roaming ? View.VISIBLE : View.GONE); mIn.setVisibility(state.activityIn ? View.VISIBLE : View.GONE); mOut.setVisibility(state.activityOut ? View.VISIBLE : View.GONE); mInoutContainer.setVisibility((state.activityIn || state.activityOut) @@ -236,6 +289,7 @@ public void onDarkChanged(ArrayList areas, float darkIntensity, int tint) mIn.setImageTintList(color); mOut.setImageTintList(color); mMobileType.setImageTintList(color); + mMobileTypeSmall.setImageTintList(color); mMobileRoaming.setImageTintList(color); mDotView.setDecorColor(tint); mDotView.setIconColor(tint, false); @@ -257,6 +311,7 @@ public void setStaticDrawableColor(int color) { mIn.setImageTintList(list); mOut.setImageTintList(list); mMobileType.setImageTintList(list); + mMobileTypeSmall.setImageTintList(list); mMobileRoaming.setImageTintList(list); mDotView.setDecorColor(color); } @@ -327,4 +382,66 @@ public MobileIconState getState() { public String toString() { return "StatusBarMobileView(slot=" + mSlot + " state=" + mState + ")"; } + + public void updateDisplayType(boolean oldStyleType) { + boolean needsLayout = false; + boolean showRoamingSpace = false; + + if (mOldStyleType != oldStyleType) { + if (mState.typeId != 0) { + if (oldStyleType) { + showOldStyle(mState); + showRoamingSpace = true; + } else { + showNewStyle(mState); + } + } else { + hideIndicators(); + } + } + if (mState.roaming) { + mMobileTypeSmall.setVisibility(View.GONE); + setMobileSignalWidth(true); + } + mMobileRoaming.setVisibility(mState.roaming ? View.VISIBLE : View.GONE); + mMobileRoamingSpace.setVisibility(showRoamingSpace || mState.roaming ? View.VISIBLE : View.GONE); + mIn.setVisibility(mState.activityIn ? View.VISIBLE : View.GONE); + mOut.setVisibility(mState.activityOut ? View.VISIBLE : View.GONE); + mInoutContainer.setVisibility((mState.activityIn || mState.activityOut) + ? View.VISIBLE : View.GONE); + + needsLayout = mOldStyleType != oldStyleType; + mOldStyleType = oldStyleType; + + if (needsLayout) { + requestLayout(); + } + } + + private void showOldStyle(MobileIconState state) { + mMobileType.setVisibility(View.GONE); + mMobileTypeSmall.setContentDescription(state.typeContentDescription); + mMobileTypeSmall.setImageResource(state.typeId); + mMobileTypeSmall.setVisibility(View.VISIBLE); + setMobileSignalWidth(false); + } + + private void showNewStyle(MobileIconState state) { + mMobileType.setVisibility(View.VISIBLE); + mMobileType.setContentDescription(state.typeContentDescription); + mMobileType.setImageResource(state.typeId); + mMobileTypeSmall.setVisibility(View.GONE); + setMobileSignalWidth(true); + } + + private void showRoaming() { + mMobileTypeSmall.setVisibility(View.GONE); + setMobileSignalWidth(true); + } + + private void hideIndicators() { + mMobileType.setVisibility(View.GONE); + mMobileTypeSmall.setVisibility(View.GONE); + setMobileSignalWidth(true); + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWifiView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWifiView.java index f3e74d92fc8a..4f70957a92e5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWifiView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWifiView.java @@ -24,6 +24,11 @@ import android.content.Context; import android.content.res.ColorStateList; import android.graphics.Rect; +import android.net.ConnectivityManager; +import android.net.Network; +import android.net.NetworkCapabilities; +import android.net.wifi.WifiInfo; +import android.net.wifi.WifiManager; import android.util.AttributeSet; import android.view.Gravity; import android.view.LayoutInflater; @@ -48,6 +53,7 @@ public class StatusBarWifiView extends BaseStatusBarWifiView implements DarkRece /// Contains the main icon layout private LinearLayout mWifiGroup; private ImageView mWifiIcon; + private ImageView mWifiStandard; private ImageView mIn; private ImageView mOut; private View mInoutContainer; @@ -57,6 +63,9 @@ public class StatusBarWifiView extends BaseStatusBarWifiView implements DarkRece private String mSlot; @StatusBarIconView.VisibleState private int mVisibleState = STATE_HIDDEN; + private boolean mShowWifiStandard; + private WifiManager mWifiManager; + private ConnectivityManager mConnectivityManager; public static StatusBarWifiView fromContext(Context context, String slot) { LayoutInflater inflater = LayoutInflater.from(context); @@ -68,15 +77,17 @@ public static StatusBarWifiView fromContext(Context context, String slot) { } public StatusBarWifiView(Context context) { - super(context); + this(context, null); } public StatusBarWifiView(Context context, AttributeSet attrs) { - super(context, attrs); + this(context, attrs, 0); } public StatusBarWifiView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); + mWifiManager = context.getSystemService(WifiManager.class); + mConnectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); } public void setSlot(String slot) { @@ -87,6 +98,7 @@ public void setSlot(String slot) { public void setStaticDrawableColor(int color) { ColorStateList list = ColorStateList.valueOf(color); mWifiIcon.setImageTintList(list); + mWifiStandard.setImageTintList(list); mIn.setImageTintList(list); mOut.setImageTintList(list); mDotView.setDecorColor(color); @@ -151,6 +163,7 @@ public void getDrawingRect(Rect outRect) { private void init() { mWifiGroup = findViewById(R.id.wifi_group); mWifiIcon = findViewById(R.id.wifi_signal); + mWifiStandard = findViewById(R.id.wifi_standard); mIn = findViewById(R.id.wifi_in); mOut = findViewById(R.id.wifi_out); mSignalSpacer = findViewById(R.id.wifi_signal_spacer); @@ -192,6 +205,12 @@ public void applyWifiState(WifiIconState state) { private boolean updateState(WifiIconState state) { setContentDescription(state.contentDescription); + if (mShowWifiStandard && isWifiConnected()) { + mWifiStandard.setVisibility(View.VISIBLE); + setWifiStandard(); + } else { + mWifiStandard.setVisibility(View.GONE); + } if (mState.resId != state.resId && state.resId >= 0) { mWifiIcon.setImageDrawable(mContext.getDrawable(state.resId)); } @@ -217,6 +236,12 @@ private boolean updateState(WifiIconState state) { private void initViewState() { setContentDescription(mState.contentDescription); + if (mShowWifiStandard && isWifiConnected()) { + mWifiStandard.setVisibility(View.VISIBLE); + setWifiStandard(); + } else { + mWifiStandard.setVisibility(View.GONE); + } if (mState.resId >= 0) { mWifiIcon.setImageDrawable(mContext.getDrawable(mState.resId)); } @@ -230,20 +255,74 @@ private void initViewState() { setVisibility(mState.visible ? View.VISIBLE : View.GONE); } + private void setWifiStandard() { + int wifiStandard = getWifiStandard(mState); + if (wifiStandard >= 4) { + int identifier = getResources().getIdentifier("ic_wifi_standard_" + wifiStandard, + "drawable", getContext().getPackageName()); + if (identifier > 0) { + mWifiStandard.setImageDrawable(mContext.getDrawable(identifier)); + } + } + } + + private int getWifiStandard(WifiIconState state) { + WifiInfo wifiInfo = mWifiManager.getConnectionInfo(); + return state.visible ? wifiInfo.getWifiStandard() : -1; + } + @Override public void onDarkChanged(ArrayList areas, float darkIntensity, int tint) { int areaTint = getTint(areas, this, tint); ColorStateList color = ColorStateList.valueOf(areaTint); mWifiIcon.setImageTintList(color); + mWifiStandard.setImageTintList(color); mIn.setImageTintList(color); mOut.setImageTintList(color); mDotView.setDecorColor(areaTint); mDotView.setIconColor(areaTint, false); } - @Override public String toString() { return "StatusBarWifiView(slot=" + mSlot + " state=" + mState + ")"; } + + public void updateWifiState(boolean showWifiStandard) { + boolean needsLayout = false; + if (mShowWifiStandard != showWifiStandard) { + if (showWifiStandard && isWifiConnected()) { + mWifiStandard.setVisibility(View.VISIBLE); + setWifiStandard(); + } else { + mWifiStandard.setVisibility(View.GONE); + } + } + + mIn.setVisibility(mState.activityIn ? View.VISIBLE : View.GONE); + mOut.setVisibility(mState.activityOut ? View.VISIBLE : View.GONE); + mInoutContainer.setVisibility( + (mState.activityIn || mState.activityOut) ? View.VISIBLE : View.GONE); + mAirplaneSpacer.setVisibility(mState.airplaneSpacerVisible ? View.VISIBLE : View.GONE); + mSignalSpacer.setVisibility(mState.signalSpacerVisible ? View.VISIBLE : View.GONE); + setVisibility(mState.visible ? View.VISIBLE : View.GONE); + + needsLayout = mShowWifiStandard != showWifiStandard; + mShowWifiStandard = showWifiStandard; + + if (needsLayout) { + requestLayout(); + } + } + + private boolean isWifiConnected() { + final Network network = mConnectivityManager.getActiveNetwork(); + if (network != null) { + NetworkCapabilities capabilities = mConnectivityManager.getNetworkCapabilities(network); + return capabilities != null && + capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI); + } else { + return false; + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/CallbackHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/CallbackHandler.java index 163878004177..bb1ad3487433 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/CallbackHandler.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/CallbackHandler.java @@ -48,6 +48,7 @@ public class CallbackHandler extends Handler implements EmergencyListener, Signa private static final int MSG_MOBILE_DATA_ENABLED_CHANGED = 5; private static final int MSG_ADD_REMOVE_EMERGENCY = 6; private static final int MSG_ADD_REMOVE_SIGNAL = 7; + private static final int MSG_IMS_STATE_CHANGED = 8; private static final int HISTORY_SIZE = 64; private static final SimpleDateFormat SSDF = new SimpleDateFormat("MM-dd HH:mm:ss.SSS"); @@ -115,6 +116,11 @@ public void handleMessage(Message msg) { mSignalCallbacks.remove((SignalCallback) msg.obj); } break; + case MSG_IMS_STATE_CHANGED: + for (SignalCallback signalCluster : mSignalCallbacks) { + signalCluster.setImsIcon((ImsIconState) msg.obj); + } + break; } } @@ -253,6 +259,11 @@ public void setIsAirplaneMode(IconState icon) { obtainMessage(MSG_AIRPLANE_MODE_CHANGED, icon).sendToTarget(); } + @Override + public void setImsIcon(ImsIconState icon) { + obtainMessage(MSG_IMS_STATE_CHANGED, icon).sendToTarget();; + } + void setListening(EmergencyListener listener, boolean listening) { obtainMessage(MSG_ADD_REMOVE_EMERGENCY, listening ? 1 : 0, 0, listener).sendToTarget(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java index 5cf1abc18274..832c8c410cb0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java @@ -19,12 +19,18 @@ import static com.android.settingslib.mobile.MobileMappings.getIconKey; import static com.android.settingslib.mobile.MobileMappings.mapIconSets; +import android.content.BroadcastReceiver; +import android.content.ContentResolver; import android.content.Context; import android.content.Intent; +import android.content.IntentFilter; import android.database.ContentObserver; import android.net.NetworkCapabilities; +import android.net.Uri; import android.os.Handler; +import android.os.UserHandle; import android.os.Looper; +import android.provider.Settings; import android.provider.Settings.Global; import android.telephony.CellSignalStrength; import android.telephony.CellSignalStrengthCdma; @@ -32,6 +38,13 @@ import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; +import android.telephony.ims.ImsException; +import android.telephony.ims.ImsMmTelManager; +import android.telephony.ims.ImsReasonInfo; +import android.telephony.ims.ImsRegistrationAttributes; +import android.telephony.ims.ImsStateCallback; +import android.telephony.ims.RegistrationManager.RegistrationCallback; +import android.telephony.ims.feature.MmTelFeature; import android.text.Html; import android.text.TextUtils; import android.util.Log; @@ -39,12 +52,14 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.settingslib.SignalIcon.MobileIconGroup; import com.android.settingslib.graph.SignalDrawable; +import com.android.settingslib.mobile.MobileMappings; import com.android.settingslib.mobile.MobileMappings.Config; import com.android.settingslib.mobile.MobileStatusTracker; import com.android.settingslib.mobile.MobileStatusTracker.MobileStatus; import com.android.settingslib.mobile.MobileStatusTracker.SubscriptionDefaults; import com.android.settingslib.mobile.TelephonyIcons; import com.android.settingslib.net.SignalStrengthUtil; +import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.util.CarrierConfigTracker; @@ -62,6 +77,7 @@ public class MobileSignalController extends SignalController list) { + if (list != null && list.size() == 2) { + if (list.get(0).getSubscriptionId() > list.get(1).getSubscriptionId()) + return true; + } + return false; + } + @GuardedBy("mLock") @VisibleForTesting void setCurrentSubscriptionsLocked(List subscriptions) { @@ -935,6 +1027,7 @@ public int compare(SubscriptionInfo lhs, SubscriptionInfo rhs) { : lhs.getSimSlotIndex() - rhs.getSimSlotIndex(); } }); + mSwap = isSwap(subscriptions); Log.i( TAG, String.format( @@ -1064,6 +1157,7 @@ private void notifyAllListeners() { } mWifiSignalController.notifyListeners(); mEthernetSignalController.notifyListeners(); + updateImsIcon(); } /** @@ -1125,6 +1219,7 @@ private void updateConnectivity() { && !mConnectedTransports.get(NetworkCapabilities.TRANSPORT_ETHERNET); mCallbackHandler.setConnectivityStatus(mNoDefaultNetwork, !mInetCondition, mNoNetworksAvailable); + updateImsIcon(); } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/SignalCallback.kt b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/SignalCallback.kt index 599beecb0e00..aa35cb4538f1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/SignalCallback.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/SignalCallback.kt @@ -91,6 +91,9 @@ interface SignalCallback { @JvmDefault fun setMobileDataEnabled(enabled: Boolean) {} + @JvmDefault + fun setImsIcon(icon: ImsIconState) {} + /** * Callback for listeners to be able to update the connectivity status * @param noDefaultNetwork whether there is any default network. @@ -184,4 +187,38 @@ data class IconState( .append("contentDescription=").append(contentDescription).append(']') .toString() } -} \ No newline at end of file +} + +data class ImsIconState( + @JvmField var visible: Boolean, + @JvmField val volteVisible: Boolean, + @JvmField val vowifiVisible: Boolean, + @JvmField val volteIcon: Int, + @JvmField val vowifiIcon: Int, + @JvmField val contentDescription: String +) { + constructor( + volteVisible: Boolean, + vowifiVisible: Boolean, + volteIcon: Int, + vowifiIcon: Int, + contentDescription: String + ): this( + volteVisible || vowifiVisible, + volteVisible, + vowifiVisible, + volteIcon, + vowifiIcon, + contentDescription) {} + + override fun toString(): String { + return java.lang.StringBuilder("ImsIconState[") + .append("visible=").append(visible) + .append(",volteVisible=").append(volteVisible) + .append(",vowifiVisible=").append(vowifiVisible) + .append(",volteIcon=").append(volteIcon) + .append(",vowifiIcon=").append(vowifiIcon) + .append(",contentDescription=").append(contentDescription) + .append(']').toString() + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt index d88f07ca304c..0aa051ac993d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt @@ -512,7 +512,7 @@ class PrivacyDotViewController @Inject constructor( if (shouldShow && state.designatedCorner != null) { showDotView(state.designatedCorner, true) } else if (!shouldShow && state.designatedCorner != null) { - hideDotView(state.designatedCorner, true) + hideDotView(state.designatedCorner, false) } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java index 59022c0ffbf2..6514d5b6f080 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java @@ -289,7 +289,7 @@ private void postInstantAppNotif( .setComponent(aiaComponent) .setAction(Intent.ACTION_VIEW) .addCategory(Intent.CATEGORY_BROWSABLE) - .addCategory("unique:" + System.currentTimeMillis()) + .setIdentifier("unique:" + System.currentTimeMillis()) .putExtra(Intent.EXTRA_PACKAGE_NAME, appInfo.packageName) .putExtra( Intent.EXTRA_VERSION_CODE, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/MediaNotificationProcessor.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/MediaNotificationProcessor.java index 732c115571f8..0cfb5023cb48 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/MediaNotificationProcessor.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/MediaNotificationProcessor.java @@ -62,6 +62,11 @@ public static Palette.Swatch findBackgroundSwatch(Palette palette) { // by default we use the dominant palette Palette.Swatch dominantSwatch = palette.getDominantSwatch(); if (dominantSwatch == null) { + // try to bring up the muted color of the cover art before going for the white variant + Palette.Swatch mutedSwatch = palette.getMutedSwatch(); + if (mutedSwatch != null) { + return mutedSwatch; + } return new Palette.Swatch(Color.WHITE, 100); } @@ -103,10 +108,9 @@ public static Palette.Swatch findBackgroundSwatch(Palette palette) { * @return Builder that generates the {@link Palette} for the media artwork. */ public static Palette.Builder generateArtworkPaletteBuilder(Bitmap artwork) { - // for the background we only take the left side of the image to ensure + // for the background we only take the full image to ensure // a smooth transition return Palette.from(artwork) - .setRegion(0, 0, artwork.getWidth() / 2, artwork.getHeight()) .clearFilters() // we want all colors, red / white / black ones too! .resizeBitmapArea(RESIZE_BITMAP_AREA); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt index aeeeb4fb054d..1b1cee80d9d6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt @@ -103,9 +103,11 @@ private class SensitiveContentCoordinatorImpl @Inject constructor( else -> lockscreenUserManager.needsSeparateWorkChallenge(notifUserId) } } - val needsRedaction = lockscreenUserManager.needsRedaction(entry) + val isSecure = entry.sbn.isContentSecure + val needsRedaction = isSecure || lockscreenUserManager.needsRedaction(entry) val isSensitive = userPublic && needsRedaction - entry.setSensitive(isSensitive, deviceSensitive) + entry.setSensitive(isSensitive, isSecure || deviceSensitive) + entry.row.setForceHideContents(isSecure) } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt index 745d6fe1d624..b8e090c64022 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt @@ -102,6 +102,6 @@ class NotifUiAdjustmentProvider @Inject constructor( isConversation = entry.ranking.isConversation, isSnoozeEnabled = isSnoozeEnabled, isMinimized = isEntryMinimized(entry), - needsRedaction = lockscreenUserManager.needsRedaction(entry), + needsRedaction = entry.sbn.isContentSecure || lockscreenUserManager.needsRedaction(entry), ) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java index 47cdde444dbd..86c6ce512cd5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java @@ -211,7 +211,7 @@ private void inflateContentViews( params.setUseIncreasedCollapsedHeight(useIncreasedCollapsedHeight); params.setUseLowPriority(isLowPriority); - if (mNotificationLockscreenUserManager.needsRedaction(entry)) { + if (mNotificationLockscreenUserManager.needsRedaction(entry) || entry.getSbn().getIsContentSecure()) { params.requireContentViews(FLAG_CONTENT_VIEW_PUBLIC); } else { params.markContentViewsFreeable(FLAG_CONTENT_VIEW_PUBLIC); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java index 558fd62c78bf..62572ef6434c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java @@ -19,6 +19,7 @@ import static com.android.systemui.statusbar.StatusBarState.SHADE; import android.app.NotificationManager; +import android.content.Context; import android.content.ContentResolver; import android.database.ContentObserver; import android.hardware.display.AmbientDisplayConfiguration; @@ -27,8 +28,10 @@ import android.os.RemoteException; import android.os.UserHandle; import android.provider.Settings; +import android.provider.Telephony.Sms; import android.service.dreams.IDreamManager; import android.service.notification.StatusBarNotification; +import android.telecom.TelecomManager; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; @@ -68,12 +71,16 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter private final NotificationInterruptLogger mLogger; private final NotifPipelineFlags mFlags; private final KeyguardNotificationVisibilityProvider mKeyguardNotificationVisibilityProvider; + private boolean mLessBoringHeadsUp; + private TelecomManager mTm; + private Context mContext; @VisibleForTesting protected boolean mUseHeadsUp = false; @Inject public NotificationInterruptStateProviderImpl( + Context context, ContentResolver contentResolver, PowerManager powerManager, IDreamManager dreamManager, @@ -93,6 +100,8 @@ public NotificationInterruptStateProviderImpl( mAmbientDisplayConfiguration = ambientDisplayConfiguration; mStatusBarStateController = statusBarStateController; mKeyguardStateController = keyguardStateController; + mContext = context; + mTm = (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE); mHeadsUpManager = headsUpManager; mLogger = logger; mFlags = flags; @@ -113,6 +122,9 @@ public void onChange(boolean selfChange) { mHeadsUpManager.releaseAllImmediately(); } } + mLessBoringHeadsUp = Settings.System.getIntForUser(mContentResolver, + Settings.System.LESS_BORING_HEADS_UP, 0, + UserHandle.USER_CURRENT) == 1; } }; @@ -124,6 +136,11 @@ public void onChange(boolean selfChange) { mContentResolver.registerContentObserver( Settings.Global.getUriFor(SETTING_HEADS_UP_TICKER), true, headsUpObserver); + mContentResolver.registerContentObserver( + Settings.System.getUriFor(Settings.System.LESS_BORING_HEADS_UP), + true, + headsUpObserver); + } headsUpObserver.onChange(true); // set up } @@ -283,6 +300,10 @@ private boolean shouldHeadsUpWhenAwake(NotificationEntry entry) { return false; } + if (shouldSkipHeadsUp(sbn)) { + return false; + } + if (isSnoozedPackage(sbn)) { mLogger.logNoHeadsUpPackageSnoozed(entry); return false; @@ -321,6 +342,28 @@ private boolean shouldHeadsUpWhenAwake(NotificationEntry entry) { return true; } + private void setUseLessBoringHeadsUp(boolean lessBoring) { + mLessBoringHeadsUp = lessBoring; + } + + public boolean shouldSkipHeadsUp(StatusBarNotification sbn) { + boolean isImportantHeadsUp = false; + String notificationPackageName = sbn.getPackageName(); + isImportantHeadsUp = notificationPackageName.equals(getDefaultDialerPackage(mTm)) + || notificationPackageName.equals(getDefaultSmsPackage(mContext)) + || notificationPackageName.contains("clock"); + return !mStatusBarStateController.isDozing() && mLessBoringHeadsUp && !isImportantHeadsUp; + } + + private static String getDefaultSmsPackage(Context ctx) { + return Sms.getDefaultSmsPackage(ctx); + // for reference, there's also a new RoleManager api with getDefaultSmsPackage(context, userid) + } + + private static String getDefaultDialerPackage(TelecomManager tm) { + return tm != null ? tm.getDefaultDialerPackage() : ""; + } + /** * Whether or not the notification should "pulse" on the user's display when the phone is * dozing. This displays the ambient view of the notification. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java index 639187790ae0..212f8ef233b1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java @@ -52,6 +52,7 @@ import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.concurrent.Executor; import javax.inject.Inject; @@ -535,7 +536,7 @@ private void maybeNotifyOnNotificationExpansionChanged(final String key, State s return; } if (loggedExpansionState != null - && state.mIsExpanded == loggedExpansionState) { + && Objects.equals(state.mIsExpanded, loggedExpansionState)) { return; } mLoggedExpansionState.put(key, state.mIsExpanded); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java index 6138265c641d..085416298e8e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java @@ -357,6 +357,8 @@ public Float get(ExpandableNotificationRow object) { private float mTopRoundnessDuringLaunchAnimation; private float mBottomRoundnessDuringLaunchAnimation; + private boolean mForceHideContents = false; + /** * Returns whether the given {@code statusBarNotification} is a system notification. * Note, this should be run in the background thread if possible as it makes multiple IPC @@ -2039,12 +2041,15 @@ void ensureGutsInflated() { private void updateChildrenVisibility() { boolean hideContentWhileLaunching = mExpandAnimationRunning && mGuts != null && mGuts.isExposed(); - mPrivateLayout.setVisibility(!mShowingPublic && !mIsSummaryWithChildren + mPrivateLayout.setVisibility(!mForceHideContents + && !mShowingPublic + && !mIsSummaryWithChildren && !hideContentWhileLaunching ? VISIBLE : INVISIBLE); if (mChildrenContainer != null) { - mChildrenContainer.setVisibility(!mShowingPublic && mIsSummaryWithChildren - && !hideContentWhileLaunching ? VISIBLE - : INVISIBLE); + mChildrenContainer.setVisibility(!mForceHideContents + && !mShowingPublic + && mIsSummaryWithChildren + && !hideContentWhileLaunching ? VISIBLE : INVISIBLE); } // The limits might have changed if the view suddenly became a group or vice versa updateLimits(); @@ -2230,6 +2235,7 @@ public boolean isSoundEffectsEnabled() { } public boolean isExpandable() { + if (mForceHideContents) return false; if (mIsSummaryWithChildren && !shouldShowPublic()) { return !mChildrenExpanded; } @@ -2392,6 +2398,7 @@ public boolean performClick() { @Override public int getIntrinsicHeight() { + if (mForceHideContents) return getCollapsedHeight(); if (isUserLocked()) { return getActualHeight(); } @@ -2630,7 +2637,7 @@ public void setHideSensitive(boolean hideSensitive, boolean animated, long delay mChildrenContainer.animate().cancel(); } resetAllContentAlphas(); - mPublicLayout.setVisibility(mShowingPublic ? View.VISIBLE : View.INVISIBLE); + mPublicLayout.setVisibility((mShowingPublic || mForceHideContents) ? VISIBLE : INVISIBLE); updateChildrenVisibility(); } else { animateShowingPublic(delay, duration, mShowingPublic); @@ -2696,7 +2703,7 @@ public boolean canViewBeCleared() { } private boolean shouldShowPublic() { - return mSensitive && mHideSensitiveForIntrinsicHeight; + return mForceHideContents || (mSensitive && mHideSensitiveForIntrinsicHeight); } public void makeActionsVisibile() { @@ -3345,6 +3352,13 @@ public void setAboveShelf(boolean aboveShelf) { } } + public void setForceHideContents(boolean forceHide) { + if (mForceHideContents == forceHide) return; + mForceHideContents = forceHide; + updateChildrenVisibility(); + onNotificationUpdated(); + } + private static class NotificationViewState extends ExpandableViewState { @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java index ea0060a693b2..520a26a3ed9f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java @@ -26,20 +26,26 @@ import android.annotation.IntDef; import android.annotation.Nullable; +import android.app.ActivityManager; import android.app.INotificationManager; +import android.app.KeyguardManager; import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationChannelGroup; import android.content.Context; +import android.content.DialogInterface; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.graphics.drawable.Drawable; import android.metrics.LogMaker; import android.os.Handler; import android.os.RemoteException; +import android.os.UserHandle; +import android.provider.Settings; import android.service.notification.StatusBarNotification; import android.text.Html; import android.text.TextUtils; @@ -60,6 +66,9 @@ import com.android.internal.logging.UiEventLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.systemui.Dependency; +import com.android.settingslib.Utils; +import com.android.systemui.statusbar.phone.CentralSurfaces; +import com.android.systemui.statusbar.phone.SystemUIDialog; import com.android.systemui.R; import com.android.systemui.statusbar.notification.AssistantFeedbackController; import com.android.systemui.statusbar.notification.collection.NotificationEntry; @@ -354,6 +363,43 @@ private void bindHeader() { final View settingsButton = findViewById(R.id.info); settingsButton.setOnClickListener(getSettingsOnClickListener()); settingsButton.setVisibility(settingsButton.hasOnClickListeners() ? VISIBLE : GONE); + + // Force stop button + final View killButton = findViewById(R.id.force_stop); + boolean killButtonEnabled = Settings.System.getIntForUser( + mContext.getContentResolver(), + Settings.System.NOTIFICATION_GUTS_KILL_APP_BUTTON, 0, + UserHandle.USER_CURRENT) != 0; + if (killButtonEnabled && !isThisASystemPackage(mPackageName)) { + killButton.setVisibility(View.VISIBLE); + killButton.setOnClickListener(new View.OnClickListener() { + public void onClick(View v) { + KeyguardManager keyguardManager = (KeyguardManager) + mContext.getSystemService(Context.KEYGUARD_SERVICE); + if (keyguardManager.inKeyguardRestrictedInputMode()) { + // Don't do anything + return; + } + final SystemUIDialog killDialog = new SystemUIDialog(mContext); + killDialog.setTitle(mContext.getText(R.string.force_stop_dlg_title)); + killDialog.setMessage(mContext.getText(R.string.force_stop_dlg_text)); + killDialog.setPositiveButton( + android.R.string.ok, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + // kill pkg + ActivityManager actMan = + (ActivityManager) mContext.getSystemService( + Context.ACTIVITY_SERVICE); + actMan.forceStopPackage(mPackageName); + } + }); + killDialog.setNegativeButton(android.R.string.cancel, null); + killDialog.show(); + } + }); + } else { + killButton.setVisibility(View.GONE); + } } private OnClickListener getSettingsOnClickListener() { @@ -430,6 +476,21 @@ private void bindGroup() throws RemoteException { } } + private boolean isThisASystemPackage(String packageName) { + try { + final UserHandle userHandle = mSbn.getUser(); + PackageManager pm = CentralSurfaces.getPackageManagerForUser(mContext, + userHandle.getIdentifier()); + PackageInfo packageInfo = pm.getPackageInfo(packageName, + PackageManager.GET_SIGNATURES); + PackageInfo sys = pm.getPackageInfo("android", PackageManager.GET_SIGNATURES); + return (packageInfo != null && packageInfo.signatures != null && + sys.signatures[0].equals(packageInfo.signatures[0])); + } catch (PackageManager.NameNotFoundException e) { + return false; + } + } + private void saveImportance() { if (!mIsNonblockable) { if (mChosenImportance == null) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java index fe431377f854..0415ad9d3786 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java @@ -585,7 +585,7 @@ public int getMode() { boolean unlockingAllowed = mUpdateMonitor.isUnlockingWithBiometricAllowed(isStrongBiometric); boolean deviceDreaming = mUpdateMonitor.isDreaming(); - boolean bypass = mKeyguardBypassController.getBypassEnabled() + boolean bypass = mKeyguardBypassController.getBypassEnabledBiometric() || mAuthController.isUdfpsFingerDown(); if (!mUpdateMonitor.isDeviceInteractive()) { if (!mKeyguardViewController.isShowing()) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java index f37243adfa9e..da2f9471e20a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java @@ -201,6 +201,8 @@ static PackageManager getPackageManagerForUser(Context context, int userId) { void togglePanel(); + void toggleCameraFlash(); + void start(); boolean updateIsKeyguard(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java index 52a45d6785ca..d0bb21099b61 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java @@ -62,6 +62,7 @@ import com.android.systemui.statusbar.VibratorHelper; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent; +import com.android.systemui.statusbar.policy.FlashlightController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.KeyguardStateController; @@ -94,6 +95,7 @@ public class CentralSurfacesCommandQueueCallbacks implements CommandQueue.Callba private final PowerManager mPowerManager; private final VibratorHelper mVibratorHelper; private final Optional mVibratorOptional; + private final FlashlightController mFlashlightController; private final DisableFlagsLogger mDisableFlagsLogger; private final int mDisplayId; private final boolean mVibrateOnOpening; @@ -126,6 +128,7 @@ public class CentralSurfacesCommandQueueCallbacks implements CommandQueue.Callba PowerManager powerManager, VibratorHelper vibratorHelper, Optional vibratorOptional, + FlashlightController flashlightController, DisableFlagsLogger disableFlagsLogger, @DisplayId int displayId, SystemBarAttributesListener systemBarAttributesListener) { @@ -150,6 +153,7 @@ public class CentralSurfacesCommandQueueCallbacks implements CommandQueue.Callba mPowerManager = powerManager; mVibratorHelper = vibratorHelper; mVibratorOptional = vibratorOptional; + mFlashlightController = flashlightController; mDisableFlagsLogger = disableFlagsLogger; mDisplayId = displayId; @@ -549,6 +553,13 @@ public void togglePanel() { } } + @Override + public void toggleCameraFlash() { + if (mFlashlightController.isAvailable()) { + mFlashlightController.setFlashlight(!mFlashlightController.isEnabled()); + } + } + private boolean isGoingToSleep() { return mWakefulnessLifecycle.getWakefulness() == WakefulnessLifecycle.WAKEFULNESS_GOING_TO_SLEEP; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java index 0b63bbfec877..d855a5a26e8a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java @@ -60,6 +60,7 @@ import android.content.BroadcastReceiver; import android.content.ComponentCallbacks2; import android.content.ComponentName; +import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; @@ -67,6 +68,7 @@ import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.res.Configuration; +import android.database.ContentObserver; import android.graphics.Point; import android.hardware.devicestate.DeviceStateManager; import android.metrics.LogMaker; @@ -87,6 +89,7 @@ import android.text.TextUtils; import android.util.ArraySet; import android.util.DisplayMetrics; +import android.util.DisplayUtils; import android.util.EventLog; import android.util.IndentingPrintWriter; import android.util.Log; @@ -132,6 +135,7 @@ import com.android.systemui.AutoReinflateContainer; import com.android.systemui.CoreStartable; import com.android.systemui.DejankUtils; +import com.android.systemui.Dependency; import com.android.systemui.EventLogTags; import com.android.systemui.InitController; import com.android.systemui.Prefs; @@ -432,6 +436,13 @@ public void animateCollapsePanels(int flags, boolean force) { public void togglePanel() { mCommandQueueCallbacks.togglePanel(); } + + /** */ + @Override + public void toggleCameraFlash() { + mCommandQueueCallbacks.toggleCameraFlash(); + } + /** * The {@link StatusBarState} of the status bar. */ @@ -599,6 +610,7 @@ public int getId() { } private final DelayableExecutor mMainExecutor; + private Handler mMainHandler; private int mInteractingWindows; private @TransitionMode int mStatusBarMode; @@ -756,6 +768,7 @@ public CentralSurfacesImpl( LockscreenShadeTransitionController lockscreenShadeTransitionController, FeatureFlags featureFlags, KeyguardUnlockAnimationController keyguardUnlockAnimationController, + @Main Handler mainHandler, @Main DelayableExecutor delayableExecutor, @Main MessageRouter messageRouter, WallpaperManager wallpaperManager, @@ -839,6 +852,7 @@ public CentralSurfacesImpl( mFeatureFlags = featureFlags; mKeyguardUnlockAnimationController = keyguardUnlockAnimationController; mMainExecutor = delayableExecutor; + mMainHandler = mainHandler; mMessageRouter = messageRouter; mWallpaperManager = wallpaperManager; mJankMonitor = jankMonitor; @@ -936,6 +950,9 @@ public void start() { Log.v(TAG, "start(): no wallpaper service "); } + mSbSettingsObserver.observe(); + mSbSettingsObserver.update(); + // Set up the initial notification state. This needs to happen before CommandQueue.disable() setUpPresenter(); @@ -2763,8 +2780,11 @@ void updateResources() { mStatusBarKeyguardViewManager.updateResources(); } - mPowerButtonReveal = new PowerButtonReveal(mContext.getResources().getDimensionPixelSize( - com.android.systemui.R.dimen.physical_power_button_center_screen_location_y)); + final float scaleFactor = DisplayUtils.getScaleFactor(mContext); + int positionY = (int) (scaleFactor * mContext.getResources().getDimensionPixelSize( + R.dimen.physical_power_button_center_screen_location_y)); + + mPowerButtonReveal = new PowerButtonReveal(positionY); } // Visibility reporting @@ -3880,7 +3900,7 @@ public void updateScrimController() { boolean launchingAffordanceWithPreview = mNotificationPanelViewController.isLaunchingAffordanceWithPreview(); - mScrimController.setLaunchingAffordanceWithPreview(launchingAffordanceWithPreview); + mScrimController.setLaunchingAffordanceWithPreview(true); if (mStatusBarKeyguardViewManager.isShowingAlternateAuth()) { if (mState == StatusBarState.SHADE || mState == StatusBarState.SHADE_LOCKED @@ -3989,6 +4009,44 @@ public boolean isDeviceInteractive() { return mDeviceInteractive; } + private SbSettingsObserver mSbSettingsObserver = new SbSettingsObserver(mMainHandler); + private class SbSettingsObserver extends ContentObserver { + SbSettingsObserver(Handler handler) { + super(handler); + } + + void observe() { + ContentResolver resolver = mContext.getContentResolver(); + resolver.registerContentObserver(Settings.System.getUriFor( + Settings.System.DOUBLE_TAP_SLEEP_LOCKSCREEN), + false, this, UserHandle.USER_ALL); + resolver.registerContentObserver(Settings.System.getUriFor( + Settings.System.DOUBLE_TAP_SLEEP_GESTURE), + false, this, UserHandle.USER_ALL); + } + + @Override + public void onChange(boolean selfChange, Uri uri) { + super.onChange(selfChange, uri); + if (uri.equals(Settings.System.getUriFor( + Settings.System.DOUBLE_TAP_SLEEP_LOCKSCREEN)) + || uri.equals(Settings.System.getUriFor( + Settings.System.DOUBLE_TAP_SLEEP_GESTURE))) { + setDoubleTapToSleepGesture(); + } + } + + public void update() { + setDoubleTapToSleepGesture(); + } + } + + private void setDoubleTapToSleepGesture() { + if (mNotificationShadeWindowViewController != null) { + mNotificationShadeWindowViewController.setDoubleTapToSleepGesture(); + } + } + private final BroadcastReceiver mBannerActionBroadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java index 484441a1e76b..86b2c703cc78 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java @@ -18,7 +18,11 @@ import static com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentModule.OPERATOR_NAME_FRAME_VIEW; +import android.content.ContentResolver; +import android.content.Context; import android.graphics.Rect; +import android.os.UserHandle; +import android.provider.Settings; import android.view.View; import com.android.internal.annotations.VisibleForTesting; @@ -192,12 +196,13 @@ private void updateTopEntry() { boolean animateIsolation = false; if (newEntry == null) { // no heads up anymore, lets start the disappear animation - + mNotificationPanelViewController.reTickerView(false); setShown(false); animateIsolation = !isExpanded(); } else if (previousEntry == null) { // We now have a headsUp and didn't have one before. Let's start the disappear // animation + mNotificationPanelViewController.reTickerView(true); setShown(true); animateIsolation = !isExpanded(); } @@ -208,6 +213,11 @@ private void updateTopEntry() { } private void setShown(boolean isShown) { + final int clockStyle = Settings.System.getIntForUser(mClockView.getContext().getContentResolver(), + Settings.System.STATUSBAR_CLOCK_STYLE, 0, UserHandle.USER_CURRENT); + final boolean isClockVisible = Settings.System.getIntForUser(mClockView.getContext().getContentResolver(), + Settings.System.STATUSBAR_CLOCK, 1, + UserHandle.USER_CURRENT) == 1; if (mShown != isShown) { mShown = isShown; if (isShown) { @@ -217,7 +227,11 @@ private void setShown(boolean isShown) { hide(mClockView, View.INVISIBLE); mOperatorNameViewOptional.ifPresent(view -> hide(view, View.INVISIBLE)); } else { - show(mClockView); + if (clockStyle == 0 && isClockVisible) { + show(mClockView); + } else { + mClockView.setVisibility(View.GONE); + } mOperatorNameViewOptional.ifPresent(this::show); hide(mView, View.GONE, () -> { updateParentClipping(true /* shouldClip */); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java index d61c51e76e86..6129eba1c69e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java @@ -218,17 +218,7 @@ public void show(boolean resetSecuritySelection, boolean isScrimmed) { // Split up the work over multiple frames. DejankUtils.removeCallbacks(mResetRunnable); - if (mKeyguardStateController.isFaceAuthEnabled() - && !mKeyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible( - KeyguardUpdateMonitor.getCurrentUser()) - && !needsFullscreenBouncer() - && !mKeyguardUpdateMonitor.isFaceLockedOut() - && !mKeyguardUpdateMonitor.userNeedsStrongAuth() - && !mKeyguardBypassController.getBypassEnabled()) { - mHandler.postDelayed(mShowRunnable, BOUNCER_FACE_DELAY); - } else { - DejankUtils.postAfterTraversal(mShowRunnable); - } + DejankUtils.postAfterTraversal(mShowRunnable); mKeyguardStateController.notifyBouncerShowing(true /* showing */); dispatchStartingToShow(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt index b987f6815000..cc6cd1b59ce4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt @@ -92,6 +92,10 @@ open class KeyguardBypassController : Dumpable, StackScrollAlgorithm.BypassContr notifyListeners() } + var bypassEnabledBiometric: Boolean = false + + var faceUnlockMethod: Int = 0 + var bouncerShowing: Boolean = false var altBouncerShowing: Boolean = false var launchingAffordance: Boolean = false @@ -132,13 +136,24 @@ open class KeyguardBypassController : Dumpable, StackScrollAlgorithm.BypassContr } }) - val dismissByDefault = if (context.resources.getBoolean( - com.android.internal.R.bool.config_faceAuthDismissesKeyguard)) 1 else 0 - tunerService.addTunable(object : TunerService.Tunable { - override fun onTuningChanged(key: String?, newValue: String?) { - bypassEnabled = tunerService.getValue(key, dismissByDefault) != 0 - } - }, Settings.Secure.FACE_UNLOCK_DISMISSES_KEYGUARD) + if (context.resources.getBoolean( + com.android.internal.R.bool.config_faceAuthOnlyOnSecurityView)){ + bypassEnabledBiometric = false + }else{ + tunerService.addTunable(object : TunerService.Tunable { + override fun onTuningChanged(key: String?, newValue: String?) { + faceUnlockMethod = tunerService.getValue(key, 0) + } + }, Settings.Secure.FACE_UNLOCK_METHOD) + val dismissByDefault = if (context.resources.getBoolean( + com.android.internal.R.bool.config_faceAuthDismissesKeyguard)) 1 else 0 + tunerService.addTunable(object : TunerService.Tunable { + override fun onTuningChanged(key: String?, newValue: String?) { + bypassEnabledBiometric = (faceUnlockMethod == 0 && + tunerService.getValue(key, dismissByDefault) != 0) + } + }, Settings.Secure.FACE_UNLOCK_DISMISSES_KEYGUARD) + } lockscreenUserManager.addUserChangedListener( object : NotificationLockscreenUserManager.UserChangedListener { override fun onUserChanged(userId: Int) { @@ -158,8 +173,8 @@ open class KeyguardBypassController : Dumpable, StackScrollAlgorithm.BypassContr biometricSourceType: BiometricSourceType, isStrongBiometric: Boolean ): Boolean { - if (biometricSourceType == BiometricSourceType.FACE && bypassEnabled) { - val can = canBypass() + if (bypassEnabledBiometric) { + val can = biometricSourceType != BiometricSourceType.FACE || canBypass() if (!can && (isPulseExpanding || qSExpanded)) { pendingUnlock = PendingUnlock(biometricSourceType, isStrongBiometric) } @@ -183,7 +198,7 @@ open class KeyguardBypassController : Dumpable, StackScrollAlgorithm.BypassContr * If keyguard can be dismissed because of bypass. */ fun canBypass(): Boolean { - if (bypassEnabled) { + if (bypassEnabledBiometric) { return when { bouncerShowing -> true altBouncerShowing -> true @@ -209,6 +224,7 @@ open class KeyguardBypassController : Dumpable, StackScrollAlgorithm.BypassContr pw.println(" mPendingUnlock: $pendingUnlock") } pw.println(" bypassEnabled: $bypassEnabled") + pw.println(" bypassEnabledBiometric: $bypassEnabledBiometric") pw.println(" canBypass: ${canBypass()}") pw.println(" bouncerShowing: $bouncerShowing") pw.println(" altBouncerShowing: $altBouncerShowing") diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java index d24469e8421e..b76175926ea3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java @@ -95,6 +95,11 @@ public void switchIndication(KeyguardIndication indication) { switchIndication(indication == null ? null : indication.getMessage(), indication); } + public void switchIndication(KeyguardIndication indication, boolean animate) { + switchIndication(indication == null ? null : indication.getMessage(), indication, + animate, null); + } + /** * Changes the text with an animation. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java index 18877f9fb437..d34882050525 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java @@ -20,12 +20,19 @@ import static com.android.systemui.util.Utils.getStatusBarHeaderHeightKeyguard; import android.annotation.ColorInt; +import android.animation.Animator; +import android.animation.AnimatorSet; +import android.animation.ObjectAnimator; +import android.animation.AnimatorListenerAdapter; +import android.content.ContentResolver; import android.content.Context; import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Color; import android.graphics.Rect; import android.graphics.drawable.Drawable; +import android.os.UserHandle; +import android.provider.Settings; import android.util.AttributeSet; import android.util.Pair; import android.util.TypedValue; @@ -70,6 +77,7 @@ public class KeyguardStatusBarView extends RelativeLayout { private BatteryMeterView mBatteryView; private StatusIconContainer mStatusIconContainer; private ViewGroup mUserSwitcherContainer; + private boolean mHideContents; private boolean mKeyguardUserSwitcherEnabled; private boolean mKeyguardUserAvatarEnabled; @@ -221,7 +229,7 @@ private void updateVisibilities() { // we only show the multi-user switch if it's enabled through UserManager as well as // by the user. if (mIsUserSwitcherEnabled) { - mMultiUserAvatar.setVisibility(View.VISIBLE); + mMultiUserAvatar.setVisibility(mHideContents ? View.INVISIBLE : View.VISIBLE); } else { mMultiUserAvatar.setVisibility(View.GONE); } @@ -255,11 +263,15 @@ private void updateSystemIconsLayoutParams() { WindowInsets updateWindowInsets( WindowInsets insets, StatusBarContentInsetsProvider insetsProvider) { + updateWindowInsets(insetsProvider); + return super.onApplyWindowInsets(insets); + } + + private void updateWindowInsets(StatusBarContentInsetsProvider insetsProvider) { mLayoutState = LAYOUT_NONE; if (updateLayoutConsideringCutout(insetsProvider)) { requestLayout(); } - return super.onApplyWindowInsets(insets); } private boolean updateLayoutConsideringCutout(StatusBarContentInsetsProvider insetsProvider) { @@ -435,9 +447,11 @@ public boolean hasOverlappingRendering() { } /** Should only be called from {@link KeyguardStatusBarViewController}. */ - void onThemeChanged(StatusBarIconController.TintedIconManager iconManager) { + void onThemeChanged(StatusBarIconController.TintedIconManager iconManager, + StatusBarContentInsetsProvider insetsProvider) { mBatteryView.setColorsFromContext(mContext); updateIconsAndTextColors(iconManager); + updateWindowInsets(insetsProvider); } /** Should only be called from {@link KeyguardStatusBarViewController}. */ @@ -494,6 +508,94 @@ private int calculateMargin(int margin, int padding) { } } + public void toggleContents(boolean hideContents) { + boolean shouldHideContents = Settings.System.getIntForUser( + getContext().getContentResolver(), Settings.System.HIDE_LOCKSCREEN_STATUS_BAR, 0, + UserHandle.USER_CURRENT) == 1; + if (!shouldHideContents) { + hideContents = false; + } + if (mHideContents == hideContents) { + return; + } + mHideContents = hideContents; + if (mHideContents) { + Animator fadeAnimator1 = null; + if (mMultiUserAvatar.getVisibility() != View.GONE) { + fadeAnimator1 = ObjectAnimator.ofFloat(mMultiUserAvatar, "alpha", 1f, 0f); + fadeAnimator1.setDuration(500); + fadeAnimator1.setInterpolator(Interpolators.ALPHA_OUT); + fadeAnimator1.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mMultiUserAvatar.setVisibility(View.INVISIBLE); + } + }); + } + Animator fadeAnimator2 = ObjectAnimator.ofFloat(mSystemIconsContainer, "alpha", 1f, 0f); + fadeAnimator2.setDuration(500); + fadeAnimator2.setInterpolator(Interpolators.ALPHA_OUT); + fadeAnimator2.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mSystemIconsContainer.setVisibility(View.INVISIBLE); + } + }); + Animator fadeAnimator3 = null; + if (mCarrierLabel.getVisibility() != View.GONE) { + fadeAnimator3 = ObjectAnimator.ofFloat(mCarrierLabel, "alpha", 1f, 0f); + fadeAnimator3.setDuration(500); + fadeAnimator3.setInterpolator(Interpolators.ALPHA_OUT); + fadeAnimator3.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mCarrierLabel.setVisibility(View.INVISIBLE); + } + }); + } + AnimatorSet set = new AnimatorSet(); + set.playTogether(fadeAnimator2); + if (fadeAnimator3 != null) { + set.playTogether(fadeAnimator3); + } + if (fadeAnimator1 != null) { + set.playTogether(fadeAnimator1); + } + set.start(); + } else { + Animator fadeAnimator1 = null; + if (mMultiUserAvatar.getVisibility() != View.GONE) { + mMultiUserAvatar.setAlpha(0f); + mMultiUserAvatar.setVisibility(View.VISIBLE); + fadeAnimator1 = ObjectAnimator.ofFloat(mMultiUserAvatar, "alpha", 0f, 1f); + fadeAnimator1.setDuration(500); + fadeAnimator1.setInterpolator(Interpolators.ALPHA_IN); + } + mSystemIconsContainer.setAlpha(0f); + mSystemIconsContainer.setVisibility(View.VISIBLE); + Animator fadeAnimator2 = ObjectAnimator.ofFloat(mSystemIconsContainer, "alpha", 0f, 1f); + fadeAnimator2.setDuration(500); + fadeAnimator2.setInterpolator(Interpolators.ALPHA_IN); + Animator fadeAnimator3 = null; + if (mCarrierLabel.getVisibility() != View.GONE) { + mCarrierLabel.setAlpha(0f); + mCarrierLabel.setVisibility(View.VISIBLE); + fadeAnimator3 = ObjectAnimator.ofFloat(mCarrierLabel, "alpha", 0f, 1f); + fadeAnimator3.setDuration(500); + fadeAnimator3.setInterpolator(Interpolators.ALPHA_IN); + } + AnimatorSet set = new AnimatorSet(); + set.playTogether(fadeAnimator2); + if (fadeAnimator3 != null) { + set.playTogether(fadeAnimator3); + } + if (fadeAnimator1 != null) { + set.playTogether(fadeAnimator1); + } + set.start(); + } + } + /** Should only be called from {@link KeyguardStatusBarViewController}. */ void dump(PrintWriter pw, String[] args) { pw.println("KeyguardStatusBarView:"); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java index 0026b71a5304..d8764768774d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java @@ -384,7 +384,7 @@ protected void onViewDetached() { /** Should be called when the theme changes. */ public void onThemeChanged() { - mView.onThemeChanged(mTintedIconManager); + mView.onThemeChanged(mTintedIconManager, mInsetsProvider); } /** Sets whether user switcher is enabled. */ @@ -468,6 +468,7 @@ public void onAnimationEnd(Animator animation) { */ public void updateViewState() { if (!isKeyguardShowing()) { + mView.setVisibility(View.GONE); return; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java index 5a70d89908f8..4bcb61df8425 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java @@ -139,8 +139,8 @@ public AnimationFilter getAnimationFilter() { private static final int MAX_ICONS_ON_AOD = 3; /* Maximum number of icons in short shelf on lockscreen when also showing overflow dot. */ - public static final int MAX_ICONS_ON_LOCKSCREEN = 3; - public static final int MAX_STATIC_ICONS = 4; + public final int MAX_ICONS_ON_LOCKSCREEN = getResources().getInteger(R.integer.config_maxVisibleNotificationIconsOnLock); + public final int MAX_STATIC_ICONS = getResources().getInteger(R.integer.config_maxVisibleNotificationIcons); private static final int MAX_DOTS = 1; private boolean mIsStaticLayout = true; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java index 48e58fcb584d..cc88b2ce977b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java @@ -25,6 +25,7 @@ import android.app.IActivityManager; import android.app.SynchronousUserSwitchObserver; import android.app.admin.DevicePolicyManager; +import android.bluetooth.BluetoothDevice; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -32,6 +33,7 @@ import android.content.SharedPreferences; import android.content.res.Resources; import android.media.AudioManager; +import android.nfc.NfcAdapter; import android.os.Handler; import android.os.Looper; import android.os.RemoteException; @@ -46,6 +48,7 @@ import androidx.lifecycle.Observer; +import com.android.settingslib.bluetooth.CachedBluetoothDevice; import com.android.systemui.R; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dagger.qualifiers.DisplayId; @@ -123,6 +126,7 @@ public class PhoneStatusBarPolicy private final String mSlotCamera; private final String mSlotSensorsOff; private final String mSlotScreenRecord; + private final String mSlotNfc; private final int mDisplayId; private final SharedPreferences mSharedPreferences; private final DateFormatUtil mDateFormatUtil; @@ -164,8 +168,11 @@ public class PhoneStatusBarPolicy private BluetoothController mBluetooth; private AlarmManager.AlarmClockInfo mNextAlarm; + private NfcAdapter mAdapter; + private final Context mContext; + @Inject - public PhoneStatusBarPolicy(StatusBarIconController iconController, + public PhoneStatusBarPolicy(Context context, StatusBarIconController iconController, CommandQueue commandQueue, BroadcastDispatcher broadcastDispatcher, @UiBackground Executor uiBgExecutor, @Main Looper looper, @Main Resources resources, CastController castController, HotspotController hotspotController, @@ -183,6 +190,7 @@ public PhoneStatusBarPolicy(StatusBarIconController iconController, RingerModeTracker ringerModeTracker, PrivacyItemController privacyItemController, PrivacyLogger privacyLogger) { + mContext = context; mIconController = iconController; mCommandQueue = commandQueue; mBroadcastDispatcher = broadcastDispatcher; @@ -230,6 +238,7 @@ public PhoneStatusBarPolicy(StatusBarIconController iconController, mSlotSensorsOff = resources.getString(com.android.internal.R.string.status_bar_sensors_off); mSlotScreenRecord = resources.getString( com.android.internal.R.string.status_bar_screen_record); + mSlotNfc = resources.getString(com.android.internal.R.string.status_bar_nfc); mDisplayId = displayId; mSharedPreferences = sharedPreferences; @@ -243,10 +252,12 @@ public void init() { filter.addAction(AudioManager.ACTION_HEADSET_PLUG); filter.addAction(Intent.ACTION_SIM_STATE_CHANGED); + filter.addAction(BluetoothDevice.ACTION_BATTERY_LEVEL_CHANGED); filter.addAction(TelecomManager.ACTION_CURRENT_TTY_MODE_CHANGED); filter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE); filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE); filter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED); + filter.addAction(NfcAdapter.ACTION_ADAPTER_STATE_CHANGED); mBroadcastDispatcher.registerReceiverWithHandler(mIntentReceiver, filter, mHandler); Observer observer = ringer -> mHandler.post(this::updateVolumeZen); @@ -331,6 +342,12 @@ public void init() { mIconController.setIcon(mSlotScreenRecord, R.drawable.stat_sys_screen_record, null); mIconController.setIconVisibility(mSlotScreenRecord, false); + mIconController.setIcon(mSlotNfc, R.drawable.stat_sys_nfc, + mResources.getString(R.string.status_bar_nfc)); + + mIconController.setIconVisibility(mSlotNfc, false); + updateNfc(); + mRotationLockController.addCallback(this); mBluetooth.addCallback(this); mProvisionedController.addCallback(this); @@ -347,6 +364,9 @@ public void init() { mRecordingController.addCallback(this); mCommandQueue.addCallback(this); + + // Get initial user setup state + onUserSetupChanged(); } private String getManagedProfileAccessibilityString() { @@ -387,6 +407,21 @@ private String buildAlarmContentDescription() { return mResources.getString(R.string.accessibility_quick_settings_alarm, dateString); } + private NfcAdapter getAdapter() { + if (mAdapter == null) { + try { + mAdapter = NfcAdapter.getNfcAdapter(mContext); + } catch (UnsupportedOperationException e) { + mAdapter = null; + } + } + return mAdapter; + } + + private final void updateNfc() { + mIconController.setIconVisibility(mSlotNfc, getAdapter() != null && getAdapter().isEnabled()); + } + private final void updateVolumeZen() { boolean zenVisible = false; int zenIconId = 0; @@ -462,6 +497,32 @@ private final void updateBluetooth() { if (mBluetooth.isBluetoothConnected() && (mBluetooth.isBluetoothAudioActive() || !mBluetooth.isBluetoothAudioProfileOnly())) { + List connectedDevices = mBluetooth.getConnectedDevices(); + int batteryLevel = connectedDevices.isEmpty() ? + -1 : connectedDevices.get(0).getBatteryLevel(); + if (batteryLevel == 100) { + iconId = R.drawable.stat_sys_data_bluetooth_connected_battery_10; + } else if (batteryLevel >= 90) { + iconId = R.drawable.stat_sys_data_bluetooth_connected_battery_9; + } else if (batteryLevel >= 80) { + iconId = R.drawable.stat_sys_data_bluetooth_connected_battery_8; + } else if (batteryLevel >= 70) { + iconId = R.drawable.stat_sys_data_bluetooth_connected_battery_7; + } else if (batteryLevel >= 60) { + iconId = R.drawable.stat_sys_data_bluetooth_connected_battery_6; + } else if (batteryLevel >= 50) { + iconId = R.drawable.stat_sys_data_bluetooth_connected_battery_5; + } else if (batteryLevel >= 40) { + iconId = R.drawable.stat_sys_data_bluetooth_connected_battery_4; + } else if (batteryLevel >= 30) { + iconId = R.drawable.stat_sys_data_bluetooth_connected_battery_3; + } else if (batteryLevel >= 20) { + iconId = R.drawable.stat_sys_data_bluetooth_connected_battery_2; + } else if (batteryLevel >= 10) { + iconId = R.drawable.stat_sys_data_bluetooth_connected_battery_1; + } else if (batteryLevel >= 0) { + iconId = R.drawable.stat_sys_data_bluetooth_connected_battery_0; + } contentDescription = mResources.getString( R.string.accessibility_bluetooth_connected); bluetoothVisible = mBluetooth.isBluetoothEnabled(); @@ -742,6 +803,12 @@ public void onReceive(Context context, Intent intent) { case AudioManager.ACTION_HEADSET_PLUG: updateHeadsetPlug(intent); break; + case BluetoothDevice.ACTION_BATTERY_LEVEL_CHANGED: + updateBluetooth(); + break; + case NfcAdapter.ACTION_ADAPTER_STATE_CHANGED: + updateNfc(); + break; } } }; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarTransitions.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarTransitions.java index 15c6dcfd7889..b9b8fd625853 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarTransitions.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarTransitions.java @@ -31,7 +31,7 @@ public final class PhoneStatusBarTransitions extends BarTransitions { private final float mIconAlphaWhenOpaque; - private View mStartSide, mStatusIcons, mBattery; + private View mStartSide, mStatusIcons, mBattery, mClock, mCenterClock, mRightClock; private Animator mCurrentAnimation; /** @@ -44,6 +44,9 @@ public PhoneStatusBarTransitions(PhoneStatusBarView statusBarView, View backgrou mStartSide = statusBarView.findViewById(R.id.status_bar_start_side_except_heads_up); mStatusIcons = statusBarView.findViewById(R.id.statusIcons); mBattery = statusBarView.findViewById(R.id.battery); + mClock = statusBarView.findViewById(R.id.clock); + mCenterClock = statusBarView.findViewById(R.id.center_clock); + mRightClock = statusBarView.findViewById(R.id.right_clock); applyModeBackground(-1, getMode(), false /*animate*/); applyMode(getMode(), false /*animate*/); } @@ -86,7 +89,10 @@ private void applyMode(int mode, boolean animate) { anims.playTogether( animateTransitionTo(mStartSide, newAlpha), animateTransitionTo(mStatusIcons, newAlpha), - animateTransitionTo(mBattery, newAlphaBC) + animateTransitionTo(mBattery, newAlphaBC), + animateTransitionTo(mClock, newAlphaBC), + animateTransitionTo(mCenterClock, newAlphaBC), + animateTransitionTo(mRightClock, newAlphaBC) ); if (isLightsOut(mode)) { anims.setDuration(LIGHTS_OUT_DURATION); @@ -97,6 +103,9 @@ private void applyMode(int mode, boolean animate) { mStartSide.setAlpha(newAlpha); mStatusIcons.setAlpha(newAlpha); mBattery.setAlpha(newAlphaBC); + mClock.setAlpha(newAlphaBC); + mCenterClock.setAlpha(newAlphaBC); + mRightClock.setAlpha(newAlphaBC); } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java index 7aeb08dd5ddb..e67982fd975e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java @@ -48,6 +48,8 @@ public class PhoneStatusBarView extends FrameLayout { private DarkReceiver mBattery; private DarkReceiver mClock; + private DarkReceiver mClockCentre; + private DarkReceiver mClockRight; private int mRotationOrientation = -1; @Nullable private View mCutoutSpace; @@ -78,6 +80,8 @@ public void onFinishInflate() { super.onFinishInflate(); mBattery = findViewById(R.id.battery); mClock = findViewById(R.id.clock); + mClockCentre = findViewById(R.id.center_clock); + mClockRight = findViewById(R.id.right_clock); mCutoutSpace = findViewById(R.id.cutout_space_view); updateResources(); @@ -89,6 +93,8 @@ protected void onAttachedToWindow() { // Always have Battery meters in the status bar observe the dark/light modes. Dependency.get(DarkIconDispatcher.class).addDarkReceiver(mBattery); Dependency.get(DarkIconDispatcher.class).addDarkReceiver(mClock); + Dependency.get(DarkIconDispatcher.class).addDarkReceiver(mClockCentre); + Dependency.get(DarkIconDispatcher.class).addDarkReceiver(mClockRight); if (updateDisplayParameters()) { updateLayoutForCutout(); } @@ -99,6 +105,8 @@ protected void onDetachedFromWindow() { super.onDetachedFromWindow(); Dependency.get(DarkIconDispatcher.class).removeDarkReceiver(mBattery); Dependency.get(DarkIconDispatcher.class).removeDarkReceiver(mClock); + Dependency.get(DarkIconDispatcher.class).removeDarkReceiver(mClockCentre); + Dependency.get(DarkIconDispatcher.class).removeDarkReceiver(mClockRight); mDisplayCutout = null; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java index 4d1c3617ac1a..21d20432694c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java @@ -206,6 +206,7 @@ public void setUnocclusionAnimationRunning(boolean unocclusionAnimationRunning) private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; private GradientColors mColors; + private GradientColors mBehindColors; private boolean mNeedsDrawableColorUpdate; private float mAdditionalScrimBehindAlphaKeyguard = 0f; @@ -311,6 +312,7 @@ public void onUiModeChanged() { } }); mColors = new GradientColors(); + mBehindColors = new GradientColors(); } /** @@ -829,7 +831,7 @@ private void applyState() { mNotificationsAlpha = behindAlpha; mNotificationsTint = behindTint; mBehindAlpha = 1; - mBehindTint = Color.BLACK; + mBehindTint = Color.TRANSPARENT; } else { mBehindAlpha = behindAlpha; if (mState == ScrimState.KEYGUARD && mTransitionToFullShadeProgress > 0.0f) { @@ -1021,7 +1023,7 @@ protected void updateScrims() { && !mBlankScreen; mScrimInFront.setColors(mColors, animateScrimInFront); - mScrimBehind.setColors(mColors, animateBehindScrim); + mScrimBehind.setColors(mBehindColors, animateBehindScrim); mNotificationsScrim.setColors(mColors, animateScrimNotifications); dispatchBackScrimState(mScrimBehind.getViewAlpha()); @@ -1388,11 +1390,20 @@ private void updateThemeColors() { if (mScrimBehind == null) return; int background = Utils.getColorAttr(mScrimBehind.getContext(), android.R.attr.colorBackgroundFloating).getDefaultColor(); + int surfaceBackground = Utils.getColorAttr(mScrimBehind.getContext(), + com.android.internal.R.attr.colorSurfaceHeader).getDefaultColor(); int accent = Utils.getColorAccent(mScrimBehind.getContext()).getDefaultColor(); + mColors.setMainColor(background); mColors.setSecondaryColor(accent); mColors.setSupportsDarkText( ColorUtils.calculateContrast(mColors.getMainColor(), Color.WHITE) > 4.5); + + mBehindColors.setMainColor(surfaceBackground); + mBehindColors.setSecondaryColor(accent); + mBehindColors.setSupportsDarkText( + ColorUtils.calculateContrast(mBehindColors.getMainColor(), Color.WHITE) > 4.5); + mNeedsDrawableColorUpdate = true; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java index b447f0d36c10..8b70b46f3a07 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java @@ -82,7 +82,7 @@ public void prepare(ScrimState previousState) { mBehindAlpha = mClipQsScrim ? 1 : mScrimBehindAlphaKeyguard; mNotifAlpha = mClipQsScrim ? mScrimBehindAlphaKeyguard : 0; if (mClipQsScrim) { - updateScrimColor(mScrimBehind, 1f /* alpha */, Color.BLACK); + updateScrimColor(mScrimBehind, 1f /* alpha */, Color.TRANSPARENT); } } }, @@ -119,7 +119,7 @@ public void prepare(ScrimState previousState) { @Override public void prepare(ScrimState previousState) { mBehindAlpha = mClipQsScrim ? 1 : mDefaultScrimAlpha; - mBehindTint = mClipQsScrim ? Color.BLACK : Color.TRANSPARENT; + mBehindTint = Color.TRANSPARENT; mNotifAlpha = mClipQsScrim ? mDefaultScrimAlpha : 0; mNotifTint = Color.TRANSPARENT; mFrontAlpha = 0f; @@ -143,17 +143,17 @@ public void prepare(ScrimState previousState) { mBehindAlpha = mClipQsScrim ? 1 : mDefaultScrimAlpha; mNotifAlpha = 1f; mFrontAlpha = 0f; - mBehindTint = Color.BLACK; + mBehindTint = Color.TRANSPARENT; if (mClipQsScrim) { - updateScrimColor(mScrimBehind, 1f /* alpha */, Color.BLACK); + updateScrimColor(mScrimBehind, 1f /* alpha */, Color.TRANSPARENT); } } // to make sure correct color is returned before "prepare" is called @Override public int getBehindTint() { - return Color.BLACK; + return Color.TRANSPARENT; } }, @@ -249,22 +249,22 @@ public void prepare(ScrimState previousState) { mAnimateChange = !mLaunchingAffordanceWithPreview && !fromAod; mFrontTint = Color.TRANSPARENT; - mBehindTint = Color.BLACK; + mBehindTint = Color.TRANSPARENT; mBlankScreen = false; if (mDisplayRequiresBlanking && previousState == ScrimState.AOD) { // Set all scrims black, before they fade transparent. updateScrimColor(mScrimInFront, 1f /* alpha */, Color.BLACK /* tint */); - updateScrimColor(mScrimBehind, 1f /* alpha */, Color.BLACK /* tint */); + updateScrimColor(mScrimBehind, 1f /* alpha */, Color.TRANSPARENT /* tint */); // Scrims should still be black at the end of the transition. mFrontTint = Color.BLACK; - mBehindTint = Color.BLACK; + mBehindTint = Color.TRANSPARENT; mBlankScreen = true; } if (mClipQsScrim) { - updateScrimColor(mScrimBehind, 1f /* alpha */, Color.BLACK); + updateScrimColor(mScrimBehind, 1f /* alpha */, Color.TRANSPARENT); } } }, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java index d6d021ff2819..928a30968829 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java @@ -15,12 +15,16 @@ package com.android.systemui.statusbar.phone; import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_ICON; +import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_IMS; import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_MOBILE; +import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_NETWORK_TRAFFIC; import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_WIFI; import android.annotation.Nullable; import android.content.Context; import android.os.Bundle; +import android.os.UserHandle; +import android.provider.Settings; import android.text.TextUtils; import android.util.ArraySet; import android.view.Gravity; @@ -33,6 +37,7 @@ import androidx.annotation.VisibleForTesting; import com.android.internal.statusbar.StatusBarIcon; +import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.demomode.DemoModeCommandReceiver; @@ -40,9 +45,11 @@ import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver; import com.android.systemui.statusbar.BaseStatusBarWifiView; import com.android.systemui.statusbar.StatusBarIconView; +import com.android.systemui.statusbar.StatusBarImsView; import com.android.systemui.statusbar.StatusBarMobileView; import com.android.systemui.statusbar.StatusBarWifiView; import com.android.systemui.statusbar.StatusIconDisplayable; +import com.android.systemui.statusbar.connectivity.ImsIconState; import com.android.systemui.statusbar.connectivity.ui.MobileContextProvider; import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.CallIndicatorIconState; import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState; @@ -50,6 +57,8 @@ import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags; import com.android.systemui.statusbar.pipeline.wifi.ui.view.ModernStatusBarWifiView; import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.WifiViewModel; +import com.android.systemui.statusbar.policy.NetworkTrafficSB; +import com.android.systemui.tuner.TunerService; import com.android.systemui.util.Assert; import java.util.ArrayList; @@ -95,6 +104,8 @@ public interface StatusBarIconController { public void setIconVisibility(String slot, boolean b); + public void setImsIcon(String slot, ImsIconState state); + /** * Sets the live region mode for the icon * @@ -184,8 +195,10 @@ protected void onRemoveIcon(int viewIndex) { @Override public void onSetIcon(int viewIndex, StatusBarIcon icon) { - super.onSetIcon(viewIndex, icon); - mDarkIconDispatcher.applyDark((DarkReceiver) mGroup.getChildAt(viewIndex)); + View view = mGroup.getChildAt(viewIndex); + if (view instanceof StatusBarIconView) { + ((StatusBarIconView) view).set(icon); + } } @Override @@ -309,7 +322,7 @@ public TintedIconManager create(ViewGroup group, StatusBarLocation location) { /** * Turns info from StatusBarIconController into ImageViews in a ViewGroup. */ - class IconManager implements DemoModeCommandReceiver { + class IconManager implements DemoModeCommandReceiver, TunerService.Tunable { protected final ViewGroup mGroup; private final StatusBarLocation mLocation; private final StatusBarPipelineFlags mStatusBarPipelineFlags; @@ -327,6 +340,11 @@ class IconManager implements DemoModeCommandReceiver { protected DemoStatusIcons mDemoStatusIcons; protected ArrayList mBlockList = new ArrayList<>(); + private boolean mIsOldSignalStyle = false; + + private boolean mShowWifiStandard; + + private static final String SHOW_WIFI_STANDARD_ICON = Settings.Secure.SHOW_WIFI_STANDARD_ICON; public IconManager( ViewGroup group, @@ -394,6 +412,12 @@ protected StatusIconDisplayable addHolder(int index, String slot, boolean blocke case TYPE_MOBILE: return addMobileIcon(index, slot, holder.getMobileState()); + + case TYPE_NETWORK_TRAFFIC: + return addNetworkTraffic(index, slot); + + case TYPE_IMS: + return addImsIcon(index, slot, holder.getImsState()); } return null; @@ -421,6 +445,7 @@ protected StatusIconDisplayable addWifiIcon(int index, String slot, WifiIconStat view = wifiView; } mGroup.addView(view, index, onCreateLayoutParams()); + Dependency.get(TunerService.class).addTunable(this, SHOW_WIFI_STANDARD_ICON); if (mIsInDemoMode) { mDemoStatusIcons.addDemoWifiView(state); @@ -428,12 +453,19 @@ protected StatusIconDisplayable addWifiIcon(int index, String slot, WifiIconStat return view; } + protected NetworkTrafficSB addNetworkTraffic(int index, String slot) { + NetworkTrafficSB view = onCreateNetworkTraffic(slot); + mGroup.addView(view, index, onCreateLayoutParams()); + return view; + } + @VisibleForTesting protected StatusBarMobileView addMobileIcon(int index, String slot, MobileIconState state) { // Use the `subId` field as a key to query for the correct context StatusBarMobileView view = onCreateStatusBarMobileView(state.subId, slot); view.applyMobileState(state); mGroup.addView(view, index, onCreateLayoutParams()); + view.updateDisplayType(mIsOldSignalStyle); if (mIsInDemoMode) { Context mobileContext = mMobileContextProvider @@ -443,6 +475,13 @@ protected StatusBarMobileView addMobileIcon(int index, String slot, MobileIconSt return view; } + protected StatusBarImsView addImsIcon(int index, String slot, ImsIconState state) { + StatusBarImsView view = onCreateStatusBarImsView(slot); + view.applyImsState(state); + mGroup.addView(view, index, onCreateLayoutParams()); + return view; + } + private StatusBarIconView onCreateStatusBarIconView(String slot, boolean blocked) { return new StatusBarIconView(mContext, slot, null, blocked); } @@ -464,12 +503,24 @@ private StatusBarMobileView onCreateStatusBarMobileView(int subId, String slot) return view; } + private NetworkTrafficSB onCreateNetworkTraffic(String slot) { + NetworkTrafficSB view = new NetworkTrafficSB(mContext); + view.setPadding(2, 0, 2, 0); + return view; + } + + private StatusBarImsView onCreateStatusBarImsView(String slot) { + StatusBarImsView view = StatusBarImsView.fromContext(mContext, slot); + return view; + } + protected LinearLayout.LayoutParams onCreateLayoutParams() { return new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, mIconSize); } protected void destroy() { mGroup.removeAllViews(); + Dependency.get(TunerService.class).removeTunable(this); } protected void onIconExternal(int viewIndex, int height) { @@ -505,8 +556,10 @@ protected void onRemoveIcon(int viewIndex) { } public void onSetIcon(int viewIndex, StatusBarIcon icon) { - StatusBarIconView view = (StatusBarIconView) mGroup.getChildAt(viewIndex); - view.set(icon); + View view = mGroup.getChildAt(viewIndex); + if (view instanceof StatusBarIconView) { + ((StatusBarIconView) view).set(icon); + } } public void onSetIconHolder(int viewIndex, StatusBarIconHolder holder) { @@ -519,6 +572,10 @@ public void onSetIconHolder(int viewIndex, StatusBarIconHolder holder) { return; case TYPE_MOBILE: onSetMobileIcon(viewIndex, holder.getMobileState()); + return; + case TYPE_IMS: + onSetImsIcon(viewIndex, holder.getImsState()); + return; default: break; } @@ -554,6 +611,13 @@ public void onSetMobileIcon(int viewIndex, MobileIconState state) { } } + public void onSetImsIcon(int viewIndex, ImsIconState state) { + StatusBarImsView view = (StatusBarImsView) mGroup.getChildAt(viewIndex); + if (view != null) { + view.applyImsState(state); + } + } + @Override public void dispatchDemoCommand(String command, Bundle args) { if (!mDemoable) { @@ -580,7 +644,6 @@ public void onDemoModeFinished() { mIsInDemoMode = false; } } - protected void exitDemoMode() { mDemoStatusIcons.remove(); mDemoStatusIcons = null; @@ -589,5 +652,41 @@ protected void exitDemoMode() { protected DemoStatusIcons createDemoStatusIcons() { return new DemoStatusIcons((LinearLayout) mGroup, mIconSize); } + + @Override + public void onTuningChanged(String key, String newValue) { + switch (key) { + case SHOW_WIFI_STANDARD_ICON: + mShowWifiStandard = + TunerService.parseIntegerSwitch(newValue, false); + updateShowWifiStandard(); + break; + default: + break; + } + } + + private void updateShowWifiStandard() { + for (int i = 0; i < mGroup.getChildCount(); i++) { + View child = mGroup.getChildAt(i); + if (child instanceof StatusBarWifiView) { + ((StatusBarWifiView) child).updateWifiState(mShowWifiStandard); + return; + } + } + } + + protected void setMobileSignalStyle(boolean isOldSignalStyle) { + mIsOldSignalStyle = isOldSignalStyle; + } + + protected void updateMobileIconStyle() { + for (int i = 0; i < mGroup.getChildCount(); i++) { + final View child = mGroup.getChildAt(i); + if (child instanceof StatusBarMobileView) { + ((StatusBarMobileView) child).updateDisplayType(mIsOldSignalStyle); + } + } + } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java index 7c31366ba4f0..70c99ab007e8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java @@ -20,9 +20,12 @@ import android.annotation.NonNull; import android.content.Context; +import android.database.ContentObserver; import android.graphics.drawable.Icon; import android.os.Bundle; +import android.os.Handler; import android.os.UserHandle; +import android.provider.Settings; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Log; @@ -31,12 +34,14 @@ import com.android.internal.statusbar.StatusBarIcon; import com.android.systemui.Dumpable; import com.android.systemui.R; +import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.demomode.DemoMode; import com.android.systemui.demomode.DemoModeController; import com.android.systemui.dump.DumpManager; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.StatusIconDisplayable; +import com.android.systemui.statusbar.connectivity.ImsIconState; import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.CallIndicatorIconState; import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState; import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState; @@ -44,6 +49,7 @@ import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener; import com.android.systemui.tuner.TunerService; import com.android.systemui.tuner.TunerService.Tunable; +import com.android.systemui.util.settings.SystemSettings; import java.io.PrintWriter; import java.util.ArrayList; @@ -67,7 +73,10 @@ public class StatusBarIconControllerImpl implements Tunable, private final ArrayList mIconGroups = new ArrayList<>(); private final ArraySet mIconHideList = new ArraySet<>(); - private Context mContext; + private final Context mContext; + private final SystemSettings mSystemSettings; + + private boolean mIsOldSignalStyle = false; /** */ @Inject @@ -78,15 +87,44 @@ public StatusBarIconControllerImpl( ConfigurationController configurationController, TunerService tunerService, DumpManager dumpManager, - StatusBarIconList statusBarIconList) { + StatusBarIconList statusBarIconList, + @Main Handler handler, + SystemSettings systemSettings) { mStatusBarIconList = statusBarIconList; mContext = context; + mSystemSettings = systemSettings; configurationController.addCallback(this); commandQueue.addCallback(this); tunerService.addTunable(this, ICON_HIDE_LIST); demoModeController.addCallback(this); dumpManager.registerDumpable(getClass().getSimpleName(), this); + + mIsOldSignalStyle = getIsOldSignalStyle(); + final ContentObserver settingsObserver = new ContentObserver(handler) { + @Override + public void onChange(boolean selfChange) { + final boolean isOldSignalStyle = getIsOldSignalStyle(); + if (mIsOldSignalStyle == isOldSignalStyle) return; + mIsOldSignalStyle = isOldSignalStyle; + mIconGroups.forEach(group -> { + group.setMobileSignalStyle(mIsOldSignalStyle); + group.updateMobileIconStyle(); + }); + } + }; + mSystemSettings.registerContentObserverForUser( + Settings.System.USE_OLD_MOBILETYPE, + settingsObserver, + UserHandle.USER_ALL + ); + } + + private boolean getIsOldSignalStyle() { + return mSystemSettings.getIntForUser( + Settings.System.USE_OLD_MOBILETYPE, + 0, UserHandle.USER_CURRENT + ) == 1; } /** */ @@ -94,12 +132,13 @@ public StatusBarIconControllerImpl( public void addIconGroup(IconManager group) { for (IconManager existingIconManager : mIconGroups) { if (existingIconManager.mGroup == group.mGroup) { - Log.e(TAG, "Adding new IconManager for the same ViewGroup. This could cause " - + "unexpected results."); + // Remove existing icon group + removeIconGroup(existingIconManager); } } group.setController(this); + group.setMobileSignalStyle(mIsOldSignalStyle); mIconGroups.add(group); List allSlots = mStatusBarIconList.getSlots(); for (int i = 0; i < allSlots.size(); i++) { @@ -297,6 +336,23 @@ public void setExternalIcon(String slot) { mIconGroups.forEach(l -> l.onIconExternal(viewIndex, height)); } + @Override + public void setImsIcon(String slot, ImsIconState state) { + if (state == null) { + removeIcon(slot, 0); + return; + } + + StatusBarIconHolder holder = mStatusBarIconList.getIconHolder(slot, 0); + if (holder == null) { + holder = StatusBarIconHolder.fromImsIconState(state); + setIcon(slot, holder); + } else { + holder.setImsState(state); + handleSet(slot, holder); + } + } + //TODO: remove this (used in command queue and for 3rd party tiles?) public void setIcon(String slot, StatusBarIcon icon) { if (icon == null) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.java index af342dd31a76..b4ce7c260cc8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.java @@ -22,6 +22,7 @@ import android.os.UserHandle; import com.android.internal.statusbar.StatusBarIcon; +import com.android.systemui.statusbar.connectivity.ImsIconState; import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.CallIndicatorIconState; import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState; import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState; @@ -33,10 +34,14 @@ public class StatusBarIconHolder { public static final int TYPE_ICON = 0; public static final int TYPE_WIFI = 1; public static final int TYPE_MOBILE = 2; + public static final int TYPE_NETWORK_TRAFFIC = 3; + public static final int TYPE_IMS = 4; private StatusBarIcon mIcon; private WifiIconState mWifiState; private MobileIconState mMobileState; + private ImsIconState mImsState; + private int mType = TYPE_ICON; private int mTag = 0; @@ -62,6 +67,13 @@ public static StatusBarIconHolder fromResId( return holder; } + public static StatusBarIconHolder fromImsIconState(ImsIconState state) { + StatusBarIconHolder holder = new StatusBarIconHolder(); + holder.mImsState = state; + holder.mType = TYPE_IMS; + return holder; + } + /** */ public static StatusBarIconHolder fromWifiIconState(WifiIconState state) { StatusBarIconHolder holder = new StatusBarIconHolder(); @@ -95,6 +107,12 @@ public static StatusBarIconHolder fromCallIndicatorState( return holder; } + public static StatusBarIconHolder fromNetworkTraffic() { + StatusBarIconHolder holder = new StatusBarIconHolder(); + holder.mType = TYPE_NETWORK_TRAFFIC; + return holder; + } + public int getType() { return mType; } @@ -126,6 +144,15 @@ public void setMobileState(MobileIconState state) { mMobileState = state; } + @Nullable + public ImsIconState getImsState() { + return mImsState; + } + + public void setImsState(ImsIconState state) { + mImsState = state; + } + public boolean isVisible() { switch (mType) { case TYPE_ICON: @@ -134,6 +161,10 @@ public boolean isVisible() { return mWifiState.visible; case TYPE_MOBILE: return mMobileState.visible; + case TYPE_IMS: + return mImsState.visible; + case TYPE_NETWORK_TRAFFIC: + return true; default: return true; } @@ -156,6 +187,10 @@ public void setVisible(boolean visible) { case TYPE_MOBILE: mMobileState.visible = visible; break; + + case TYPE_IMS: + mImsState.visible = visible; + break; } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconList.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconList.java index 8800b05fadb7..e492f57107ff 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconList.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconList.java @@ -28,6 +28,8 @@ import java.util.Collections; import java.util.List; +import com.android.systemui.statusbar.policy.NetworkTrafficSB; + /** A class holding the list of all the system icons that could be shown in the status bar. */ public class StatusBarIconList { private final ArrayList mSlots = new ArrayList<>(); @@ -38,6 +40,9 @@ public StatusBarIconList(String[] slots) { for (int i = 0; i < N; i++) { mSlots.add(new Slot(slots[i], null)); } + + // Network traffic slot + mSlots.add(0, new Slot(NetworkTrafficSB.SLOT, StatusBarIconHolder.fromNetworkTraffic())); } /** Returns the list of current slots. */ @@ -120,9 +125,9 @@ private int findOrInsertSlot(String slot) { return i; } } - // Auto insert new items at the beginning. - mSlots.add(0, new Slot(slot, null)); - return 0; + // Auto insert new items behind network traffic + mSlots.add(1, new Slot(slot, null)); + return 1; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index 9d5392af3127..c26c341111fb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -29,6 +29,7 @@ import android.content.res.ColorStateList; import android.hardware.biometrics.BiometricSourceType; import android.os.Bundle; +import android.os.Handler; import android.os.SystemClock; import android.os.Trace; import android.view.KeyEvent; @@ -52,6 +53,7 @@ import com.android.keyguard.KeyguardViewController; import com.android.keyguard.ViewMediatorCallback; import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dock.DockManager; import com.android.systemui.dreams.DreamOverlayStateController; import com.android.systemui.flags.FeatureFlags; @@ -81,6 +83,7 @@ import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.unfold.FoldAodAnimationController; import com.android.systemui.unfold.SysUIUnfoldComponent; +import com.android.systemui.R; import java.io.PrintWriter; import java.util.ArrayList; @@ -135,6 +138,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb private final BouncerViewDelegate mBouncerViewDelegate; private final Lazy mShadeController; + private boolean mBouncerVisible = false; private final BouncerExpansionCallback mExpansionCallback = new BouncerExpansionCallback() { private boolean mBouncerAnimating; @@ -142,6 +146,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb public void onFullyShown() { mBouncerAnimating = false; updateStates(); + showFaceRecognizingMessage(); } @Override @@ -178,6 +183,7 @@ public void onVisibilityChanged(boolean isVisible) { .setBouncerShowingOverDream( isVisible && mDreamOverlayStateController.isOverlayActive()); + mBouncerVisible = isVisible; if (!isVisible) { mCentralSurfaces.setBouncerHiddenFraction(KeyguardBouncer.EXPANSION_HIDDEN); } @@ -252,6 +258,10 @@ public void onEvent(int event) { private KeyguardBypassController mBypassController; @Nullable private AlternateAuthInterceptor mAlternateAuthInterceptor; + private Handler mHandler; + private Handler mFaceRecognizingHandler; + private boolean mFaceRecognitionRunning = false; + private final KeyguardUpdateMonitorCallback mUpdateMonitorCallback = new KeyguardUpdateMonitorCallback() { @Override @@ -263,6 +273,28 @@ public void onEmergencyCallAction() { reset(true /* hideBouncerWhenShowing */); } } + + @Override + public void onBiometricRunningStateChanged(boolean running, + BiometricSourceType biometricSourceType) { + if (biometricSourceType == BiometricSourceType.FACE && + mKeyguardUpdateManager.isUnlockWithFacePossible(mKeyguardUpdateManager.getCurrentUser())){ + mFaceRecognitionRunning = running; + if (!mFaceRecognitionRunning){ + mFaceRecognizingHandler.removeCallbacksAndMessages(null); + }else{ + mFaceRecognizingHandler.postDelayed(() -> showFaceRecognizingMessage(), 100); + } + } + } + + @Override + public void onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType, + boolean isStrongBiometric) { + if (biometricSourceType == BiometricSourceType.FACE) { + hideFaceRecognizingMessage(); + } + } }; @Inject @@ -288,7 +320,9 @@ public StatusBarKeyguardViewManager( FeatureFlags featureFlags, BouncerCallbackInteractor bouncerCallbackInteractor, BouncerInteractor bouncerInteractor, - BouncerView bouncerView) { + BouncerView bouncerView, + @Main Handler handler, + @Main Handler faceRecognizingHandler) { mContext = context; mViewMediatorCallback = callback; mLockPatternUtils = lockPatternUtils; @@ -312,6 +346,8 @@ public StatusBarKeyguardViewManager( mFoldAodAnimationController = sysUIUnfoldComponent .map(SysUIUnfoldComponent::getFoldAodAnimationController).orElse(null); mIsModernBouncerEnabled = featureFlags.isEnabled(Flags.MODERN_BOUNCER); + mHandler = handler; + mFaceRecognizingHandler = faceRecognizingHandler; } @Override @@ -405,8 +441,9 @@ public void onPanelExpansionChanged(PanelExpansionChangeEvent event) { } else if (mNotificationPanelViewController.isUnlockHintRunning()) { if (mBouncer != null) { mBouncer.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN); + } else { + mBouncerInteractor.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN); } - mBouncerInteractor.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN); } else if (mStatusBarStateController.getState() == StatusBarState.SHADE_LOCKED) { // Don't expand to the bouncer. Instead transition back to the lock screen (see // CentralSurfaces#showBouncerOrLockScreenIfKeyguard) @@ -414,8 +451,9 @@ public void onPanelExpansionChanged(PanelExpansionChangeEvent event) { } else if (bouncerNeedsScrimming()) { if (mBouncer != null) { mBouncer.setExpansion(KeyguardBouncer.EXPANSION_VISIBLE); + } else { + mBouncerInteractor.setExpansion(KeyguardBouncer.EXPANSION_VISIBLE); } - mBouncerInteractor.setExpansion(KeyguardBouncer.EXPANSION_VISIBLE); } else if (mShowing && !hideBouncerOverDream) { if (!isWakeAndUnlocking() && !(mBiometricUnlockController.getMode() == MODE_DISMISS_BOUNCER) @@ -423,8 +461,9 @@ public void onPanelExpansionChanged(PanelExpansionChangeEvent event) { && !isUnlockCollapsing()) { if (mBouncer != null) { mBouncer.setExpansion(fraction); + } else { + mBouncerInteractor.setExpansion(fraction); } - mBouncerInteractor.setExpansion(fraction); } if (fraction != KeyguardBouncer.EXPANSION_HIDDEN && tracking && !mKeyguardStateController.canDismissLockScreen() @@ -432,16 +471,18 @@ public void onPanelExpansionChanged(PanelExpansionChangeEvent event) { && !bouncerIsAnimatingAway()) { if (mBouncer != null) { mBouncer.show(false /* resetSecuritySelection */, false /* scrimmed */); + } else { + mBouncerInteractor.show(/* isScrimmed= */false); } - mBouncerInteractor.show(/* isScrimmed= */false); } } else if (!mShowing && isBouncerInTransit()) { // Keyguard is not visible anymore, but expansion animation was still running. // We need to hide the bouncer, otherwise it will be stuck in transit. if (mBouncer != null) { mBouncer.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN); + } else { + mBouncerInteractor.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN); } - mBouncerInteractor.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN); } else if (mPulsing && fraction == KeyguardBouncer.EXPANSION_VISIBLE) { // Panel expanded while pulsing but didn't translate the bouncer (because we are // unlocked.) Let's simply wake-up to dismiss the lock screen. @@ -487,8 +528,9 @@ protected void showBouncerOrKeyguard(boolean hideBouncerWhenShowing) { mCentralSurfaces.hideKeyguard(); if (mBouncer != null) { mBouncer.show(true /* resetSecuritySelection */); + } else { + mBouncerInteractor.show(true); } - mBouncerInteractor.show(true); } else { mCentralSurfaces.showKeyguard(); if (hideBouncerWhenShowing) { @@ -529,8 +571,9 @@ public boolean shouldShowAltAuth() { void hideBouncer(boolean destroyView) { if (mBouncer != null) { mBouncer.hide(destroyView); + } else { + mBouncerInteractor.hide(); } - mBouncerInteractor.hide(); if (mShowing) { // If we were showing the bouncer and then aborting, we need to also clear out any // potential actions unless we actually unlocked. @@ -551,8 +594,9 @@ public void showBouncer(boolean scrimmed) { if (mShowing && !isBouncerShowing()) { if (mBouncer != null) { mBouncer.show(false /* resetSecuritySelection */, scrimmed); + } else { + mBouncerInteractor.show(scrimmed); } - mBouncerInteractor.show(scrimmed); } updateStates(); } @@ -588,9 +632,10 @@ public void dismissWithAction(OnDismissAction r, Runnable cancelAction, if (mBouncer != null) { mBouncer.setDismissAction(mAfterKeyguardGoneAction, mKeyguardGoneCancelAction); + } else { + mBouncerInteractor.setDismissAction(mAfterKeyguardGoneAction, + mKeyguardGoneCancelAction); } - mBouncerInteractor.setDismissAction(mAfterKeyguardGoneAction, - mKeyguardGoneCancelAction); mAfterKeyguardGoneAction = null; mKeyguardGoneCancelAction = null; } @@ -603,17 +648,21 @@ public void dismissWithAction(OnDismissAction r, Runnable cancelAction, if (afterKeyguardGone) { // we'll handle the dismiss action after keyguard is gone, so just show the // bouncer - mBouncerInteractor.show(/* isScrimmed= */true); - if (mBouncer != null) mBouncer.show(false /* resetSecuritySelection */); + if (mBouncer != null) { + mBouncer.show(false /* resetSecuritySelection */); + } else { + mBouncerInteractor.show(/* isScrimmed= */true); + } } else { // after authentication success, run dismiss action with the option to defer // hiding the keyguard based on the return value of the OnDismissAction - mBouncerInteractor.setDismissAction( - mAfterKeyguardGoneAction, mKeyguardGoneCancelAction); - mBouncerInteractor.show(/* isScrimmed= */true); if (mBouncer != null) { mBouncer.showWithDismissAction(mAfterKeyguardGoneAction, mKeyguardGoneCancelAction); + } else { + mBouncerInteractor.setDismissAction( + mAfterKeyguardGoneAction, mKeyguardGoneCancelAction); + mBouncerInteractor.show(/* isScrimmed= */true); } // bouncer will handle the dismiss action, so we no longer need to track it here mAfterKeyguardGoneAction = null; @@ -624,6 +673,11 @@ public void dismissWithAction(OnDismissAction r, Runnable cancelAction, } } updateStates(); + mHandler.postDelayed(() -> { + if (mBouncerVisible) { + mKeyguardUpdateManager.updateFaceListeningStateForBehavior(mBouncerVisible); + } + }, 100); } private boolean isWakeAndUnlocking() { @@ -717,8 +771,9 @@ public void onStartedGoingToSleep() { public void onFinishedGoingToSleep() { if (mBouncer != null) { mBouncer.onScreenTurnedOff(); + } else { + mBouncerInteractor.onScreenTurnedOff(); } - mBouncerInteractor.onScreenTurnedOff(); } @Override @@ -830,8 +885,9 @@ public void startPreHideAnimation(Runnable finishRunnable) { if (bouncerIsShowing()) { if (mBouncer != null) { mBouncer.startPreHideAnimation(finishRunnable); + } else { + mBouncerInteractor.startDisappearAnimation(finishRunnable); } - mBouncerInteractor.startDisappearAnimation(finishRunnable); mCentralSurfaces.onBouncerPreHideAnimation(); // We update the state (which will show the keyguard) only if an animation will run on @@ -1102,13 +1158,15 @@ protected void updateStates() { if (bouncerDismissible || !showing || remoteInputActive) { if (mBouncer != null) { mBouncer.setBackButtonEnabled(true); + } else { + mBouncerInteractor.setBackButtonEnabled(true); } - mBouncerInteractor.setBackButtonEnabled(true); } else { if (mBouncer != null) { mBouncer.setBackButtonEnabled(false); + } else { + mBouncerInteractor.setBackButtonEnabled(false); } - mBouncerInteractor.setBackButtonEnabled(false); } } @@ -1274,8 +1332,9 @@ public void onCancelClicked() { public void notifyKeyguardAuthenticated(boolean strongAuth) { if (mBouncer != null) { mBouncer.notifyKeyguardAuthenticated(strongAuth); + } else { + mBouncerInteractor.notifyKeyguardAuthenticated(strongAuth); } - mBouncerInteractor.notifyKeyguardAuthenticated(strongAuth); if (mAlternateAuthInterceptor != null && isShowingAlternateAuthOrAnimating()) { resetAlternateAuth(false); @@ -1283,6 +1342,17 @@ public void notifyKeyguardAuthenticated(boolean strongAuth) { } } + private void showFaceRecognizingMessage(){ + if (mFaceRecognitionRunning && + mKeyguardUpdateManager.isUnlockWithFacePossible(mKeyguardUpdateManager.getCurrentUser())) { + setKeyguardMessage(mContext.getString(R.string.face_unlock_recognizing), null); + } + } + + private void hideFaceRecognizingMessage(){ + setKeyguardMessage("", null); + } + /** Display security message to relevant KeyguardMessageArea. */ public void setKeyguardMessage(String message, ColorStateList colorState) { if (isShowingAlternateAuth()) { @@ -1292,8 +1362,9 @@ public void setKeyguardMessage(String message, ColorStateList colorState) { } else { if (mBouncer != null) { mBouncer.showMessage(message, colorState); + } else { + mBouncerInteractor.showMessage(message, colorState); } - mBouncerInteractor.showMessage(message, colorState); } } @@ -1340,8 +1411,9 @@ && bouncerIsScrimmed()) public void updateResources() { if (mBouncer != null) { mBouncer.updateResources(); + } else { + mBouncerInteractor.updateResources(); } - mBouncerInteractor.updateResources(); } public void dump(PrintWriter pw) { @@ -1426,9 +1498,9 @@ public boolean onTouch(MotionEvent event) { public void updateKeyguardPosition(float x) { if (mBouncer != null) { mBouncer.updateKeyguardPosition(x); + } else { + mBouncerInteractor.setKeyguardPosition(x); } - - mBouncerInteractor.setKeyguardPosition(x); } private static class DismissWithActionRequest { @@ -1470,9 +1542,9 @@ public void requestFp(boolean request, int udfpsColor) { public boolean isBouncerInTransit() { if (mBouncer != null) { return mBouncer.inTransit(); + } else { + return mBouncerInteractor.isInTransit(); } - - return mBouncerInteractor.isInTransit(); } /** @@ -1481,9 +1553,9 @@ public boolean isBouncerInTransit() { public boolean bouncerIsShowing() { if (mBouncer != null) { return mBouncer.isShowing(); + } else { + return mBouncerInteractor.isFullyShowing(); } - - return mBouncerInteractor.isFullyShowing(); } /** @@ -1492,9 +1564,9 @@ public boolean bouncerIsShowing() { public boolean bouncerIsScrimmed() { if (mBouncer != null) { return mBouncer.isScrimmed(); + } else { + return mBouncerInteractor.isScrimmed(); } - - return mBouncerInteractor.isScrimmed(); } /** @@ -1503,9 +1575,10 @@ public boolean bouncerIsScrimmed() { public boolean bouncerIsAnimatingAway() { if (mBouncer != null) { return mBouncer.isAnimatingAway(); + } else { + return mBouncerInteractor.isAnimatingAway(); } - return mBouncerInteractor.isAnimatingAway(); } /** @@ -1514,9 +1587,9 @@ public boolean bouncerIsAnimatingAway() { public boolean bouncerWillDismissWithAction() { if (mBouncer != null) { return mBouncer.willDismissWithAction(); + } else { + return mBouncerInteractor.willDismissWithAction(); } - - return mBouncerInteractor.willDismissWithAction(); } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java index 64ca270558e8..e6e93553a4a1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java @@ -383,7 +383,7 @@ public boolean suppressAwakeHeadsUp(NotificationEntry entry) { @Override public boolean suppressAwakeInterruptions(NotificationEntry entry) { - return isDeviceInVrMode(); + return isDeviceInVrMode() || entry.getSbn().getIsContentSecure(); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java index 492734e93dca..68b41bc61b39 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java @@ -27,6 +27,7 @@ import com.android.systemui.R; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.statusbar.connectivity.IconState; +import com.android.systemui.statusbar.connectivity.ImsIconState; import com.android.systemui.statusbar.connectivity.MobileDataIndicators; import com.android.systemui.statusbar.connectivity.NetworkController; import com.android.systemui.statusbar.connectivity.SignalCallback; @@ -56,6 +57,8 @@ public class StatusBarSignalPolicy implements SignalCallback, private final String mSlotVpn; private final String mSlotNoCalling; private final String mSlotCallStrength; + private final String mSlotRoaming = "roaming"; + private final String mSlotIms; private final Context mContext; private final StatusBarIconController mIconController; @@ -70,6 +73,9 @@ public class StatusBarSignalPolicy implements SignalCallback, private boolean mHideWifi; private boolean mHideEthernet; private boolean mActivityEnabled; + private boolean mHideVpn; + private boolean mHideRoaming; + private boolean mHideIms; // Track as little state as possible, and only for padding purposes private boolean mIsAirplaneMode = false; @@ -106,6 +112,7 @@ public StatusBarSignalPolicy( mSlotCallStrength = mContext.getString(com.android.internal.R.string.status_bar_call_strength); mActivityEnabled = mContext.getResources().getBoolean(R.bool.config_showActivity); + mSlotIms = mContext.getString(com.android.internal.R.string.status_bar_ims); } /** Call to initilaize and register this classw with the system. */ @@ -126,7 +133,7 @@ public void destroy() { } private void updateVpn() { - boolean vpnVisible = mSecurityController.isVpnEnabled(); + boolean vpnVisible = mSecurityController.isVpnEnabled() && !mHideVpn; int vpnIconId = currentVpnIconId(mSecurityController.isVpnBranded()); mIconController.setIcon(mSlotVpn, vpnIconId, @@ -156,13 +163,21 @@ public void onTuningChanged(String key, String newValue) { boolean hideMobile = hideList.contains(mSlotMobile); boolean hideWifi = hideList.contains(mSlotWifi); boolean hideEthernet = hideList.contains(mSlotEthernet); + boolean hideVpn = hideList.contains(mSlotVpn); + boolean hideRoaming = hideList.contains(mSlotRoaming); + boolean hideIms = hideList.contains(mSlotIms); if (hideAirplane != mHideAirplane || hideMobile != mHideMobile - || hideEthernet != mHideEthernet || hideWifi != mHideWifi) { + || hideEthernet != mHideEthernet || hideWifi != mHideWifi + || hideVpn != mHideVpn || hideRoaming != mHideRoaming + || hideIms != mHideIms) { mHideAirplane = hideAirplane; mHideMobile = hideMobile; mHideEthernet = hideEthernet; mHideWifi = hideWifi; + mHideVpn = hideVpn; + mHideRoaming = hideRoaming; + mHideIms = hideIms; // Re-register to get new callbacks. mNetworkController.removeCallback(this); mNetworkController.addCallback(this); @@ -267,7 +282,7 @@ public void setMobileDataIndicators(@NonNull MobileDataIndicators indicators) { state.contentDescription = indicators.statusIcon.contentDescription; state.typeContentDescription = indicators.typeContentDescription; state.showTriangle = indicators.showTriangle; - state.roaming = indicators.roaming; + state.roaming = indicators.roaming && !mHideRoaming; state.activityIn = indicators.activityIn && mActivityEnabled; state.activityOut = indicators.activityOut && mActivityEnabled; @@ -409,6 +424,16 @@ public void setMobileDataEnabled(boolean enabled) { // Don't care. } + @Override + public void setImsIcon(ImsIconState icon) { + if (icon.visible && !mHideIms) { + mIconController.setImsIcon(mSlotIms, icon); + mIconController.setIconVisibility(mSlotIms, true); + } else { + mIconController.setIconVisibility(mSlotIms, false); + } + } + /** * Stores the statusbar state for no Calling & SMS. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java index e1215ee95238..982f954d2f33 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java @@ -37,6 +37,11 @@ import android.util.ArrayMap; import android.util.IndentingPrintWriter; import android.util.SparseArray; +import android.content.ContentResolver; +import android.database.ContentObserver; +import android.os.Handler; +import android.os.UserHandle; +import android.provider.Settings; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -75,6 +80,7 @@ import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController; import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallListener; import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager; +import com.android.systemui.statusbar.policy.Clock; import com.android.systemui.statusbar.policy.EncryptionHelper; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.CarrierConfigTracker; @@ -136,9 +142,39 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue private final Executor mMainExecutor; private final DumpManager mDumpManager; + private int mClockStyle; + private List mBlockedIcons = new ArrayList<>(); private Map mStartableStates = new ArrayMap<>(); + private LinearLayout mCenterClockLayout; + private View mRightClock; + private boolean mShowClock = true; + private final Handler mHandler = new Handler(); + + private class SettingsObserver extends ContentObserver { + SettingsObserver(Handler handler) { + super(handler); + } + + void observe() { + mContentResolver.registerContentObserver(Settings.System.getUriFor( + Settings.System.STATUSBAR_CLOCK), + false, this, UserHandle.USER_ALL); + mContentResolver.registerContentObserver(Settings.System.getUriFor( + Settings.System.STATUSBAR_CLOCK_STYLE), + false, this, UserHandle.USER_ALL); + } + + @Override + public void onChange(boolean selfChange) { + updateSettings(true); + } + } + + private SettingsObserver mSettingsObserver; + private ContentResolver mContentResolver; + private SignalCallback mSignalCallback = new SignalCallback() { @Override public void setIsAirplaneMode(@NonNull IconState icon) { @@ -253,14 +289,17 @@ public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { } mDarkIconManager = mDarkIconManagerFactory.create( view.findViewById(R.id.statusIcons), StatusBarLocation.HOME); + mContentResolver = getContext().getContentResolver(); + mSettingsObserver = new SettingsObserver(mHandler); mDarkIconManager.setShouldLog(true); updateBlockedIcons(); mStatusBarIconController.addIconGroup(mDarkIconManager); mEndSideContent = mStatusBar.findViewById(R.id.status_bar_end_side_content); mClockView = mStatusBar.findViewById(R.id.clock); + mCenterClockLayout = (LinearLayout) mStatusBar.findViewById(R.id.center_clock_layout); + mRightClock = mStatusBar.findViewById(R.id.right_clock); mOngoingCallChip = mStatusBar.findViewById(R.id.ongoing_call_chip); showEndSideContent(false); - showClock(false); initEmergencyCryptkeeperText(); initOperatorName(); initNotificationIconArea(); @@ -268,6 +307,8 @@ public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { new StatusBarSystemEventAnimator(mEndSideContent, getResources()); mCarrierConfigTracker.addCallback(mCarrierConfigCallback); mCarrierConfigTracker.addDefaultDataSubscriptionChangedListener(mDefaultDataListener); + mSettingsObserver.observe(); + updateSettings(false); } @VisibleForTesting @@ -415,16 +456,6 @@ public void disable(int displayId, int state1, int state2, boolean animate) { || ((diff1 & DISABLE_NOTIFICATION_ICONS) != 0)) { updateNotificationIconAreaAndCallChip(state1, animate); } - - // The clock may have already been hidden, but we might want to shift its - // visibility to GONE from INVISIBLE or vice versa - if ((diff1 & DISABLE_CLOCK) != 0 || mClockView.getVisibility() != clockHiddenMode()) { - if ((state1 & DISABLE_CLOCK) != 0) { - hideClock(animate); - } else { - showClock(animate); - } - } } protected int adjustDisableFlags(int state) { @@ -473,8 +504,10 @@ private void updateNotificationIconAreaAndCallChip(int state1, boolean animate) // Hide notifications if the disable flag is set or we have an ongoing call. if (disableNotifications || hasOngoingCall) { hideNotificationIconArea(animate); + animateHide(mClockView, animate, false); } else { showNotificationIconArea(animate); + updateClockStyle(animate); } // Show the ongoing call chip only if there is an ongoing call *and* notification icons @@ -498,13 +531,21 @@ private boolean shouldHideNotificationIcons() { } private void hideEndSideContent(boolean animate) { - animateHide(mEndSideContent, animate); + animateHide(mCenterClockLayout, animate, true); + if (mClockStyle == 2) { + animateHide(mRightClock, animate, true); + } + animateHide(mEndSideContent, animate, true); } private void showEndSideContent(boolean animate) { // Only show the system icon area if we are not currently animating int state = mAnimationScheduler.getAnimationState(); if (state == IDLE || state == SHOWING_PERSISTENT_DOT) { + animateShow(mCenterClockLayout, animate); + if (mClockStyle == 2) { + animateShow(mRightClock, animate); + } animateShow(mEndSideContent, animate); } else { // We are in the middle of a system status event animation, which will animate the @@ -513,6 +554,7 @@ private void showEndSideContent(boolean animate) { } } +/** private void hideClock(boolean animate) { animateHiddenState(mClockView, clockHiddenMode(), animate); } @@ -520,10 +562,11 @@ private void hideClock(boolean animate) { private void showClock(boolean animate) { animateShow(mClockView, animate); } +*/ /** Hides the ongoing call chip. */ public void hideOngoingCallChip(boolean animate) { - animateHiddenState(mOngoingCallChip, View.GONE, animate); + animateHide(mOngoingCallChip, animate, false); } /** Displays the ongoing call chip. */ @@ -544,16 +587,18 @@ private int clockHiddenMode() { } public void hideNotificationIconArea(boolean animate) { - animateHide(mNotificationIconAreaInner, animate); + animateHide(mNotificationIconAreaInner, animate, true); + animateHide(mCenterClockLayout, animate, true); } public void showNotificationIconArea(boolean animate) { animateShow(mNotificationIconAreaInner, animate); + animateShow(mCenterClockLayout, animate); } public void hideOperatorName(boolean animate) { if (mOperatorNameViewController != null) { - animateHide(mOperatorNameViewController.getView(), animate); + animateHide(mOperatorNameViewController.getView(), animate, true); } } @@ -564,13 +609,13 @@ public void showOperatorName(boolean animate) { } /** - * Animate a view to INVISIBLE or GONE + * Hides a View */ - private void animateHiddenState(final View v, int state, boolean animate) { + private void animateHide(final View v, boolean animate, final boolean invisible) { v.animate().cancel(); if (!animate) { v.setAlpha(0f); - v.setVisibility(state); + v.setVisibility(invisible ? View.INVISIBLE : View.GONE); return; } @@ -579,20 +624,16 @@ private void animateHiddenState(final View v, int state, boolean animate) { .setDuration(160) .setStartDelay(0) .setInterpolator(Interpolators.ALPHA_OUT) - .withEndAction(() -> v.setVisibility(state)); - } - - /** - * Hides a view. - */ - private void animateHide(final View v, boolean animate) { - animateHiddenState(v, View.INVISIBLE, animate); + .withEndAction(() -> v.setVisibility(invisible ? View.INVISIBLE : View.GONE)); } /** * Shows a view, and synchronizes the animation with Keyguard exit animations, if applicable. */ private void animateShow(View v, boolean animate) { + if (v instanceof Clock && !((Clock)v).isClockVisible()) { + return; + } v.animate().cancel(); v.setVisibility(View.VISIBLE); if (!animate) { @@ -683,6 +724,31 @@ private void updateStatusBarLocation(int left, int right) { mLocationPublisher.updateStatusBarMargin(leftMargin, rightMargin); } + public void updateSettings(boolean animate) { + mShowClock = Settings.System.getIntForUser(mContentResolver, + Settings.System.STATUSBAR_CLOCK, 1, + UserHandle.USER_CURRENT) == 1; + if (!mShowClock) { + mClockStyle = 1; // internally switch to centered clock layout because + // left & right will show up again after QS pulldown + } else { + mClockStyle = Settings.System.getIntForUser(mContentResolver, + Settings.System.STATUSBAR_CLOCK_STYLE, 0, + UserHandle.USER_CURRENT); + } + updateClockStyle(animate); + } + + private void updateClockStyle(boolean animate) { + if (mClockStyle == 1 || mClockStyle == 2) { + animateHide(mClockView, animate, false); + } else { + if (((Clock)mClockView).isClockVisible()) { + animateShow(mClockView, animate); + } + } + } + private final ContentObserver mVolumeSettingObserver = new ContentObserver(null) { @Override public void onChange(boolean selfChange) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java index 86e74564fba0..c96e3175026a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java @@ -19,18 +19,23 @@ import android.annotation.NonNull; import android.content.Context; import android.content.res.Resources; +import android.os.UserHandle; +import android.provider.Settings; import android.util.ArraySet; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.FrameLayout; +import android.widget.ImageView; +import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.settings.brightness.BrightnessSliderController; import com.android.systemui.settings.brightness.ToggleSlider; import com.android.systemui.shade.NotificationPanelViewController; import com.android.systemui.shade.NotificationShadeWindowView; import com.android.systemui.statusbar.NotificationShadeDepthController; +import com.android.systemui.tuner.TunerService; import java.util.Objects; import java.util.function.Consumer; @@ -41,6 +46,9 @@ public class BrightnessMirrorController implements CallbackController { + private static final String QS_SHOW_AUTO_BRIGHTNESS = + Settings.Secure.QS_SHOW_AUTO_BRIGHTNESS; + private final NotificationShadeWindowView mStatusBarWindow; private final Consumer mVisibilityCallback; private final NotificationPanelViewController mNotificationPanel; @@ -52,6 +60,9 @@ public class BrightnessMirrorController private FrameLayout mBrightnessMirror; private int mBrightnessMirrorBackgroundPadding; private int mLastBrightnessSliderWidth = -1; + private boolean mShouldShowAutoBrightness; + private boolean mIsAutomaticBrightnessAvailable; + private ImageView mIcon; public BrightnessMirrorController(NotificationShadeWindowView statusBarWindow, NotificationPanelViewController notificationPanelViewController, @@ -69,6 +80,17 @@ public BrightnessMirrorController(NotificationShadeWindowView statusBarWindow, }); mVisibilityCallback = visibilityCallback; updateResources(); + + TunerService.Tunable tunable = (key, newValue) -> { + if (QS_SHOW_AUTO_BRIGHTNESS.equals(key)) { + mShouldShowAutoBrightness = TunerService.parseIntegerSwitch(newValue, true); + updateIcon(); + } + }; + Dependency.get(TunerService.class).addTunable(tunable, QS_SHOW_AUTO_BRIGHTNESS); + + mIsAutomaticBrightnessAvailable = mBrightnessMirror.getContext().getResources().getBoolean( + com.android.internal.R.bool.config_automatic_brightness_available); } public void showMirror() { @@ -76,6 +98,7 @@ public void showMirror() { mVisibilityCallback.accept(true); mNotificationPanel.setPanelAlpha(0, true /* animate */); mDepthController.setBrightnessMirrorVisible(true); + updateIcon(); } public void hideMirror() { @@ -144,6 +167,7 @@ private BrightnessSliderController setMirrorLayout() { mBrightnessMirror.addView(controller.getRootView(), ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); + mIcon = mBrightnessMirror.findViewById(R.id.brightness_icon); return controller; } @@ -180,4 +204,21 @@ public void onUiModeChanged() { public interface BrightnessMirrorListener { void onBrightnessMirrorReinflated(View brightnessMirror); } + + private void updateIcon() { + if (mIsAutomaticBrightnessAvailable && mShouldShowAutoBrightness) { + int automatic = Settings.System.getIntForUser(mBrightnessMirror.getContext() + .getContentResolver(), + Settings.System.SCREEN_BRIGHTNESS_MODE, + Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL, + UserHandle.USER_CURRENT); + boolean isAutomatic = automatic != Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL; + mIcon.setImageResource(isAutomatic + ? com.android.systemui.R.drawable.ic_qs_brightness_auto_on + : com.android.systemui.R.drawable.ic_qs_brightness_auto_off); + mIcon.setVisibility(View.VISIBLE); + } else { + mIcon.setVisibility(View.GONE); + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java index 576962dee747..2c4e7d2baabc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java @@ -18,10 +18,12 @@ import android.app.StatusBarManager; import android.content.BroadcastReceiver; +import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.res.TypedArray; +import android.database.ContentObserver; import android.graphics.Rect; import android.icu.text.DateTimePatternGenerator; import android.os.Bundle; @@ -29,6 +31,7 @@ import android.os.Parcelable; import android.os.SystemClock; import android.os.UserHandle; +import android.provider.Settings; import android.text.Spannable; import android.text.SpannableStringBuilder; import android.text.TextUtils; @@ -36,12 +39,10 @@ import android.text.style.CharacterStyle; import android.text.style.RelativeSizeSpan; import android.util.AttributeSet; -import android.view.ContextThemeWrapper; import android.view.Display; import android.view.View; import android.widget.TextView; -import com.android.settingslib.Utils; import com.android.systemui.Dependency; import com.android.systemui.FontSizeUtils; import com.android.systemui.R; @@ -51,14 +52,12 @@ import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver; import com.android.systemui.settings.CurrentUserTracker; import com.android.systemui.statusbar.CommandQueue; -import com.android.systemui.statusbar.phone.StatusBarIconController; import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener; -import com.android.systemui.tuner.TunerService; -import com.android.systemui.tuner.TunerService.Tunable; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; +import java.util.Date; import java.util.Locale; import java.util.TimeZone; @@ -67,11 +66,9 @@ */ public class Clock extends TextView implements DemoModeCommandReceiver, - Tunable, CommandQueue.Callbacks, DarkReceiver, ConfigurationListener { - public static final String CLOCK_SECONDS = "clock_seconds"; private static final String CLOCK_SUPER_PARCELABLE = "clock_super_parcelable"; private static final String CURRENT_USER_ID = "current_user_id"; private static final String VISIBLE_BY_POLICY = "visible_by_policy"; @@ -83,25 +80,51 @@ public class Clock extends TextView implements private final CommandQueue mCommandQueue; private int mCurrentUserId; - private boolean mClockVisibleByPolicy = true; - private boolean mClockVisibleByUser = true; + protected boolean mClockVisibleByPolicy = true; + protected boolean mClockVisibleByUser = true; + protected boolean mClockHideableByUser = true; - private boolean mAttached; + protected boolean mAttached; private boolean mScreenReceiverRegistered; - private Calendar mCalendar; - private String mContentDescriptionFormatString; - private SimpleDateFormat mClockFormat; + + protected Calendar mCalendar; + protected String mContentDescriptionFormatString; + protected SimpleDateFormat mClockFormat; private SimpleDateFormat mContentDescriptionFormat; - private Locale mLocale; + protected Locale mLocale; private DateTimePatternGenerator mDateTimePatternGenerator; - private static final int AM_PM_STYLE_NORMAL = 0; - private static final int AM_PM_STYLE_SMALL = 1; - private static final int AM_PM_STYLE_GONE = 2; - - private final int mAmPmStyle; + public static final int AM_PM_STYLE_GONE = 0; + public static final int AM_PM_STYLE_SMALL = 1; + public static final int AM_PM_STYLE_NORMAL = 2; + + private static int AM_PM_STYLE = AM_PM_STYLE_GONE; + + public static final int CLOCK_DATE_DISPLAY_GONE = 0; + public static final int CLOCK_DATE_DISPLAY_SMALL = 1; + public static final int CLOCK_DATE_DISPLAY_NORMAL = 2; + + public static final int CLOCK_DATE_STYLE_REGULAR = 0; + public static final int CLOCK_DATE_STYLE_LOWERCASE = 1; + public static final int CLOCK_DATE_STYLE_UPPERCASE = 2; + + public static final int STYLE_CLOCK_LEFT = 0; + public static final int STYLE_CLOCK_CENTER = 1; + public static final int STYLE_CLOCK_RIGHT = 2; + public static final int STYLE_DATE_LEFT = 0; + public static final int STYLE_DATE_RIGHT = 1; + + protected int mClockDateDisplay = CLOCK_DATE_DISPLAY_GONE; + protected int mClockDateStyle = CLOCK_DATE_STYLE_REGULAR; + protected int mClockStyle = STYLE_CLOCK_LEFT; + protected String mClockDateFormat = null; + protected int mClockDatePosition; + protected boolean mShowClock = true; + private int mAmPmStyle; + protected boolean mQsHeader; private boolean mShowSeconds; private Handler mSecondsHandler; + private SettingsObserver mSettingsObserver; // Fields to cache the width so the clock remains at an approximately constant width private int mCharsAtCurrentWidth = -1; @@ -114,6 +137,46 @@ public class Clock extends TextView implements private final BroadcastDispatcher mBroadcastDispatcher; + protected class SettingsObserver extends ContentObserver { + SettingsObserver(Handler handler) { + super(handler); + } + + void observe() { + ContentResolver resolver = mContext.getContentResolver(); + resolver.registerContentObserver(Settings.System.getUriFor( + Settings.System.STATUSBAR_CLOCK), + false, this, UserHandle.USER_ALL); + resolver.registerContentObserver(Settings.System.getUriFor( + Settings.System.STATUSBAR_CLOCK_STYLE), + false, this, UserHandle.USER_ALL); + resolver.registerContentObserver(Settings.System.getUriFor( + Settings.System.STATUSBAR_CLOCK_SECONDS), + false, this, UserHandle.USER_ALL); + resolver.registerContentObserver(Settings.System.getUriFor( + Settings.System.STATUSBAR_CLOCK_AM_PM_STYLE), + false, this, UserHandle.USER_ALL); + resolver.registerContentObserver(Settings.System.getUriFor( + Settings.System.STATUSBAR_CLOCK_DATE_DISPLAY), + false, this, UserHandle.USER_ALL); + resolver.registerContentObserver(Settings.System.getUriFor( + Settings.System.STATUSBAR_CLOCK_DATE_STYLE), + false, this, UserHandle.USER_ALL); + resolver.registerContentObserver(Settings.System.getUriFor( + Settings.System.STATUSBAR_CLOCK_DATE_FORMAT), + false, this, UserHandle.USER_ALL); + resolver.registerContentObserver(Settings.System.getUriFor( + Settings.System.STATUSBAR_CLOCK_DATE_POSITION), + false, this, UserHandle.USER_ALL); + updateSettings(); + } + + @Override + public void onChange(boolean selfChange) { + updateSettings(); + } + } + public Clock(Context context, AttributeSet attrs) { this(context, attrs, 0); } @@ -138,6 +201,7 @@ public void onUserSwitched(int newUserId) { mCurrentUserId = newUserId; } }; + updateSettings(); } @Override @@ -193,8 +257,6 @@ protected void onAttachedToWindow() { // The receiver will return immediately if the view does not have a Handler yet. mBroadcastDispatcher.registerReceiverWithHandler(mIntentReceiver, filter, Dependency.get(Dependency.TIME_TICK_HANDLER), UserHandle.ALL); - Dependency.get(TunerService.class).addTunable(this, CLOCK_SECONDS, - StatusBarIconController.ICON_HIDE_LIST); mCommandQueue.addCallback(this); mCurrentUserTracker.startTracking(); mCurrentUserId = mCurrentUserTracker.getCurrentUserId(); @@ -205,9 +267,11 @@ protected void onAttachedToWindow() { mContentDescriptionFormatString = ""; mDateTimePatternGenerator = null; - // Make sure we update to the current time - updateClock(); - updateClockVisibility(); + if (mSettingsObserver == null) { + mSettingsObserver = new SettingsObserver(new Handler()); + } + mSettingsObserver.observe(); + updateSettings(); updateShowSeconds(); } @@ -224,8 +288,8 @@ protected void onDetachedFromWindow() { } if (mAttached) { mBroadcastDispatcher.unregisterReceiver(mIntentReceiver); + getContext().getContentResolver().unregisterContentObserver(mSettingsObserver); mAttached = false; - Dependency.get(TunerService.class).removeTunable(this); mCommandQueue.removeCallback(this); mCurrentUserTracker.stopTracking(); } @@ -254,10 +318,10 @@ public void onReceive(Context context, Intent intent) { handler.post(() -> { if (!newLocale.equals(mLocale)) { mLocale = newLocale; - // Force refresh of dependent variables. - mContentDescriptionFormatString = ""; mDateTimePatternGenerator = null; } + updateSettings(); + return; }); } handler.post(() -> updateClock()); @@ -274,8 +338,10 @@ public void setVisibility(int visibility) { } public void setClockVisibleByUser(boolean visible) { - mClockVisibleByUser = visible; - updateClockVisibility(); + if (mClockHideableByUser) { + mClockVisibleByUser = visible; + updateClockVisibility(); + } } public void setClockVisibilityByPolicy(boolean visible) { @@ -287,12 +353,21 @@ private boolean shouldBeVisible() { return mClockVisibleByPolicy && mClockVisibleByUser; } - private void updateClockVisibility() { - boolean visible = shouldBeVisible(); + protected void updateClockVisibility() { + boolean visible = ((mClockStyle == STYLE_CLOCK_LEFT) || (mQsHeader)) + && mShowClock && mClockVisibleByPolicy && mClockVisibleByUser; int visibility = visible ? View.VISIBLE : View.GONE; super.setVisibility(visibility); } + public boolean isClockVisible() { + return mClockVisibleByPolicy && mClockVisibleByUser; + } + + public void setClockHideableByUser(boolean value) { + mClockHideableByUser = value; + } + final void updateClock() { if (mDemoMode) return; mCalendar.setTimeInMillis(System.currentTimeMillis()); @@ -332,18 +407,6 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { } } - @Override - public void onTuningChanged(String key, String newValue) { - if (CLOCK_SECONDS.equals(key)) { - mShowSeconds = TunerService.parseIntegerSwitch(newValue, false); - updateShowSeconds(); - } else if (StatusBarIconController.ICON_HIDE_LIST.equals(key)) { - setClockVisibleByUser(!StatusBarIconController.getIconHideList(getContext(), newValue) - .contains("clock")); - updateClockVisibility(); - } - } - @Override public void disable(int displayId, int state1, int state2, boolean animate) { if (displayId != getDisplay().getDisplayId()) { @@ -361,13 +424,6 @@ public void onDarkChanged(ArrayList areas, float darkIntensity, int tint) setTextColor(mNonAdaptedColor); } - // Update text color based when shade scrim changes color. - public void onColorsChanged(boolean lightTheme) { - final Context context = new ContextThemeWrapper(mContext, - lightTheme ? R.style.Theme_SystemUI_LightWallpaper : R.style.Theme_SystemUI); - setTextColor(Utils.getColorAttrDefaultColor(context, R.attr.wallpaperTextColor)); - } - @Override public void onDensityOrFontScaleChanged() { FontSizeUtils.updateFontSize(this, R.dimen.status_bar_clock_size); @@ -418,6 +474,7 @@ private final CharSequence getSmallTime() { final char MAGIC1 = '\uEF00'; final char MAGIC2 = '\uEF01'; + SimpleDateFormat sdf; final String formatSkeleton = mShowSeconds ? is24 ? "Hms" : "hms" : is24 ? "Hm" : "hm"; @@ -455,15 +512,65 @@ private final CharSequence getSmallTime() { + "a" + MAGIC2 + format.substring(b + 1); } } - mClockFormat = new SimpleDateFormat(format); + mClockFormat = sdf = new SimpleDateFormat(format); + } else { + sdf = mClockFormat; + } + + CharSequence dateString = null; + + String result = ""; + String timeResult = sdf.format(mCalendar.getTime()); + String dateResult = ""; + + if (mClockDateDisplay != CLOCK_DATE_DISPLAY_GONE) { + Date now = new Date(); + + if (mClockDateFormat == null || mClockDateFormat.isEmpty()) { + // Set dateString to short uppercase Weekday if empty + dateString = DateFormat.format("EEE", now); + } else { + dateString = DateFormat.format(mClockDateFormat, now); + } + if (mClockDateStyle == CLOCK_DATE_STYLE_LOWERCASE) { + // When Date style is small, convert date to uppercase + dateResult = dateString.toString().toLowerCase(); + } else if (mClockDateStyle == CLOCK_DATE_STYLE_UPPERCASE) { + dateResult = dateString.toString().toUpperCase(); + } else { + dateResult = dateString.toString(); + } + result = (mClockDatePosition == STYLE_DATE_LEFT) ? dateResult + " " + timeResult + : timeResult + " " + dateResult; + } else { + // No date, just show time + result = timeResult; + } + + SpannableStringBuilder formatted = new SpannableStringBuilder(result); + + if (mClockDateDisplay != CLOCK_DATE_DISPLAY_NORMAL) { + if (dateString != null) { + int dateStringLen = dateString.length(); + int timeStringOffset = (mClockDatePosition == STYLE_DATE_RIGHT) + ? timeResult.length() + 1 : 0; + if (mClockDateDisplay == CLOCK_DATE_DISPLAY_GONE) { + formatted.delete(0, dateStringLen); + } else { + if (mClockDateDisplay == CLOCK_DATE_DISPLAY_SMALL) { + CharacterStyle style = new RelativeSizeSpan(0.7f); + formatted.setSpan(style, timeStringOffset, + timeStringOffset + dateStringLen, + Spannable.SPAN_EXCLUSIVE_INCLUSIVE); + } + } + } } - String result = mClockFormat.format(mCalendar.getTime()); if (mAmPmStyle != AM_PM_STYLE_NORMAL) { int magic1 = result.indexOf(MAGIC1); int magic2 = result.indexOf(MAGIC2); if (magic1 >= 0 && magic2 > magic1) { - SpannableStringBuilder formatted = new SpannableStringBuilder(result); if (mAmPmStyle == AM_PM_STYLE_GONE) { formatted.delete(magic1, magic2+1); } else { @@ -475,12 +582,77 @@ private final CharSequence getSmallTime() { formatted.delete(magic2, magic2 + 1); formatted.delete(magic1, magic1 + 1); } - return formatted; } } + return formatted; + } + + private void updateStatus() { + if (mAttached) { + updateClock(); + updateShowSeconds(); + } + } + + protected void updateSettings() { + ContentResolver resolver = mContext.getContentResolver(); + + mShowClock = Settings.System.getIntForUser(resolver, + Settings.System.STATUSBAR_CLOCK, 1, + UserHandle.USER_CURRENT) == 1; + if (mQsHeader) { + mShowClock = true; // QSHeader clock may override show clock + } - return result; + mShowSeconds = Settings.System.getIntForUser(resolver, + Settings.System.STATUSBAR_CLOCK_SECONDS, 0, + UserHandle.USER_CURRENT) == 1; + if (!mShowClock) { + mClockStyle = 1; // internally switch to centered clock layout because + // left & right will show up again after QS pulldown + } else { + mClockStyle = Settings.System.getIntForUser(resolver, + Settings.System.STATUSBAR_CLOCK_STYLE, STYLE_CLOCK_LEFT, + UserHandle.USER_CURRENT); + } + + boolean is24hour = DateFormat.is24HourFormat(mContext); + int amPmStyle = Settings.System.getIntForUser(resolver, + Settings.System.STATUSBAR_CLOCK_AM_PM_STYLE, + AM_PM_STYLE_GONE, + UserHandle.USER_CURRENT); + mAmPmStyle = is24hour ? AM_PM_STYLE_GONE : amPmStyle; + mContentDescriptionFormatString = ""; + + mClockDateDisplay = Settings.System.getIntForUser(resolver, + Settings.System.STATUSBAR_CLOCK_DATE_DISPLAY, CLOCK_DATE_DISPLAY_GONE, + UserHandle.USER_CURRENT); + + mClockDateStyle = Settings.System.getIntForUser(resolver, + Settings.System.STATUSBAR_CLOCK_DATE_STYLE, CLOCK_DATE_STYLE_REGULAR, + UserHandle.USER_CURRENT); + + mClockDateFormat = Settings.System.getString(resolver, + Settings.System.STATUSBAR_CLOCK_DATE_FORMAT); + + mClockDatePosition = Settings.System.getIntForUser(resolver, + Settings.System.STATUSBAR_CLOCK_DATE_POSITION, STYLE_DATE_LEFT, + UserHandle.USER_CURRENT); + + if (mAttached) { + updateClockVisibility(); + updateClock(); + updateShowSeconds(); + } + } + + public boolean isClockDateEnabled() { + return isClockVisible() && mClockDateDisplay != CLOCK_DATE_DISPLAY_GONE; + } + + public void setQsHeader() { + mQsHeader = true; } private boolean mDemoMode; @@ -545,4 +717,3 @@ public void run() { } }; } - diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ClockCenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ClockCenter.java new file mode 100644 index 000000000000..b429865d9cbe --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ClockCenter.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.policy; + +import android.app.StatusBarManager; +import android.content.Context; +import android.os.Handler; +import android.util.AttributeSet; +import android.view.View; + +import com.android.systemui.Dependency; + +public class ClockCenter extends Clock { + + private boolean mClockVisibleByPolicy = true; + private boolean mClockVisibleByUser = true; + + public ClockCenter(Context context) { + this(context, null); + } + + public ClockCenter(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public ClockCenter(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + public void setClockVisibleByUser(boolean visible) { + mClockVisibleByUser = visible; + updateClockVisibility(); + } + + public void setClockVisibilityByPolicy(boolean visible) { + mClockVisibleByPolicy = visible; + updateClockVisibility(); + } + + protected void updateClockVisibility() { + boolean visible = mClockStyle == STYLE_CLOCK_CENTER && mShowClock + && mClockVisibleByPolicy && mClockVisibleByUser; + int visibility = visible ? View.VISIBLE : View.GONE; + setVisibility(visibility); + } + + public void disable(int state1, int state2, boolean animate) { + boolean clockVisibleByPolicy = (state1 & StatusBarManager.DISABLE_CLOCK) == 0; + if (clockVisibleByPolicy != mClockVisibleByPolicy) { + setClockVisibilityByPolicy(clockVisibleByPolicy); + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ClockRight.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ClockRight.java new file mode 100644 index 000000000000..0765a532baec --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ClockRight.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.policy; + +import android.app.StatusBarManager; +import android.content.Context; +import android.os.Handler; +import android.util.AttributeSet; +import android.view.View; + +import com.android.systemui.Dependency; + +public class ClockRight extends Clock { + + private boolean mClockVisibleByPolicy = true; + private boolean mClockVisibleByUser = true; + + public ClockRight(Context context) { + this(context, null); + } + + public ClockRight(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public ClockRight(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + public void setClockVisibleByUser(boolean visible) { + mClockVisibleByUser = visible; + updateClockVisibility(); + } + + public void setClockVisibilityByPolicy(boolean visible) { + mClockVisibleByPolicy = visible; + updateClockVisibility(); + } + + protected void updateClockVisibility() { + boolean visible = mClockStyle == STYLE_CLOCK_RIGHT && mShowClock + && mClockVisibleByPolicy && mClockVisibleByUser; + int visibility = visible ? View.VISIBLE : View.GONE; + setVisibility(visibility); + } + + public void disable(int state1, int state2, boolean animate) { + boolean clockVisibleByPolicy = (state1 & StatusBarManager.DISABLE_CLOCK) == 0; + if (clockVisibleByPolicy != mClockVisibleByPolicy) { + setClockVisibilityByPolicy(clockVisibleByPolicy); + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkTraffic.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkTraffic.java new file mode 100644 index 000000000000..749bb11957df --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkTraffic.java @@ -0,0 +1,367 @@ +package com.android.systemui.statusbar.policy; + +import java.text.DecimalFormat; + +import android.content.BroadcastReceiver; +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.res.Resources; +import android.database.ContentObserver; +import android.graphics.drawable.Drawable; +import android.graphics.PorterDuff.Mode; +import android.graphics.Rect; +import android.graphics.Typeface; +import android.view.Gravity; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; +import android.net.TrafficStats; +import android.os.Handler; +import android.os.UserHandle; +import android.os.Message; +import android.os.SystemClock; +import android.provider.Settings; +import android.text.Spanned; +import android.text.SpannableString; +import android.text.style.RelativeSizeSpan; +import android.text.TextUtils; +import android.util.AttributeSet; +import android.util.TypedValue; +import android.view.View; +import android.widget.TextView; + +import com.android.systemui.R; + +/* +* +* Seeing how an Integer object in java requires at least 16 Bytes, it seemed awfully wasteful +* to only use it for a single boolean. 32-bits is plenty of room for what we need it to do. +* +*/ +public class NetworkTraffic extends TextView { + + private static final int INTERVAL = 1500; //ms + private static final int KB = 1024; + private static final int MB = KB * KB; + private static final int GB = MB * KB; + private static final String symbol = "/S"; + + private final int mWidth; + + protected boolean mIsEnabled; + private boolean mAttached; + private long totalRxBytes; + private long totalTxBytes; + private long lastUpdateTime; + private int mAutoHideThreshold; + protected int mTintColor; + + private boolean mScreenOn = true; + protected boolean mVisible = true; + private ConnectivityManager mConnectivityManager; + + private Handler mTrafficHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + long timeDelta = SystemClock.elapsedRealtime() - lastUpdateTime; + + if (timeDelta < INTERVAL * .95) { + if (msg.what != 1) { + // we just updated the view, nothing further to do + return; + } + if (timeDelta < 1) { + // Can't div by 0 so make sure the value displayed is minimal + timeDelta = Long.MAX_VALUE; + } + } + lastUpdateTime = SystemClock.elapsedRealtime(); + + // Calculate the data rate from the change in total bytes and time + long newTotalRxBytes = TrafficStats.getTotalRxBytes(); + long newTotalTxBytes = TrafficStats.getTotalTxBytes(); + long rxData = newTotalRxBytes - totalRxBytes; + long txData = newTotalTxBytes - totalTxBytes; + + if (shouldHide(rxData, txData, timeDelta)) { + setText(""); + setVisibility(View.INVISIBLE); + mVisible = false; + } else if (shouldShowUpload(rxData, txData, timeDelta)) { + // Show information for uplink if it's called for + CharSequence output = formatOutput(timeDelta, txData, symbol); + + // Update view if there's anything new to show + if (output != getText()) { + setText(output); + } + makeVisible(); + } else { + // Add information for downlink if it's called for + CharSequence output = formatOutput(timeDelta, rxData, symbol); + + // Update view if there's anything new to show + if (output != getText()) { + setText(output); + } + makeVisible(); + } + + // Post delayed message to refresh in ~1000ms + totalRxBytes = newTotalRxBytes; + totalTxBytes = newTotalTxBytes; + clearHandlerCallbacks(); + mTrafficHandler.postDelayed(mRunnable, INTERVAL); + } + + private CharSequence formatOutput(long timeDelta, long data, String symbol) { + long speed = (long)(data / (timeDelta / 1000F)); + + return formatDecimal(speed); + } + + private CharSequence formatDecimal(long speed) { + DecimalFormat decimalFormat; + String unit; + String formatSpeed; + SpannableString spanUnitString; + SpannableString spanSpeedString; + + if (speed >= 1000 * MB) { + unit = "GB"; + decimalFormat = new DecimalFormat("0.00"); + formatSpeed = decimalFormat.format(speed / (float)GB); + } else if (speed >= 100 * MB) { + decimalFormat = new DecimalFormat("000"); + unit = "MB"; + formatSpeed = decimalFormat.format(speed / (float)MB); + } else if (speed >= 10 * MB) { + decimalFormat = new DecimalFormat("00.0"); + unit = "MB"; + formatSpeed = decimalFormat.format(speed / (float)MB); + } else if (speed >= 1000 * KB) { + decimalFormat = new DecimalFormat("0.00"); + unit = "MB"; + formatSpeed = decimalFormat.format(speed / (float)MB); + } else if (speed >= 100 * KB) { + decimalFormat = new DecimalFormat("000"); + unit = "KB"; + formatSpeed = decimalFormat.format(speed / (float)KB); + } else if (speed >= 10 * KB) { + decimalFormat = new DecimalFormat("00.0"); + unit = "KB"; + formatSpeed = decimalFormat.format(speed / (float)KB); + } else { + decimalFormat = new DecimalFormat("0.00"); + unit = "KB"; + formatSpeed = decimalFormat.format(speed / (float)KB); + } + spanSpeedString = new SpannableString(formatSpeed); + spanSpeedString.setSpan(getSpeedRelativeSizeSpan(), 0, (formatSpeed).length(), + Spanned.SPAN_INCLUSIVE_INCLUSIVE); + + spanUnitString = new SpannableString(unit + symbol); + spanUnitString.setSpan(getUnitRelativeSizeSpan(), 0, (unit + symbol).length(), + Spanned.SPAN_INCLUSIVE_INCLUSIVE); + return TextUtils.concat(spanSpeedString, "\n", spanUnitString); + } + + private boolean shouldHide(long rxData, long txData, long timeDelta) { + long speedRxKB = (long)(rxData / (timeDelta / 1000f)) / KB; + long speedTxKB = (long)(txData / (timeDelta / 1000f)) / KB; + return !getConnectAvailable() || + (speedRxKB < mAutoHideThreshold && + speedTxKB < mAutoHideThreshold); + } + + private boolean shouldShowUpload(long rxData, long txData, long timeDelta) { + long speedRxKB = (long)(rxData / (timeDelta / 1000f)) / KB; + long speedTxKB = (long)(txData / (timeDelta / 1000f)) / KB; + + return (speedTxKB > speedRxKB); + } + }; + + protected boolean restoreViewQuickly() { + return getConnectAvailable() && mAutoHideThreshold == 0; + } + + protected void makeVisible() { + setVisibility(View.VISIBLE); + mVisible = true; + } + + /* + * @hide + */ + public NetworkTraffic(Context context) { + this(context, null); + } + + /* + * @hide + */ + public NetworkTraffic(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + /* + * @hide + */ + public NetworkTraffic(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + final Resources resources = getResources(); + mTintColor = getCurrentTextColor(); + mWidth = resources.getDimensionPixelSize(R.dimen.network_traffic_width); + setMode(); + Handler mHandler = new Handler(); + mConnectivityManager = + (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); + SettingsObserver settingsObserver = new SettingsObserver(mHandler); + settingsObserver.observe(); + update(); + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + if (!mAttached) { + mAttached = true; + IntentFilter filter = new IntentFilter(); + filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); + filter.addAction(Intent.ACTION_SCREEN_OFF); + filter.addAction(Intent.ACTION_SCREEN_ON); + mContext.registerReceiver(mIntentReceiver, filter, null, getHandler()); + } + update(); + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + if (mAttached) { + mContext.unregisterReceiver(mIntentReceiver); + mAttached = false; + } + } + + protected RelativeSizeSpan getSpeedRelativeSizeSpan() { + return new RelativeSizeSpan(0.78f); + } + + protected RelativeSizeSpan getUnitRelativeSizeSpan() { + return new RelativeSizeSpan(0.70f); + } + + private Runnable mRunnable = new Runnable() { + @Override + public void run() { + mTrafficHandler.sendEmptyMessage(0); + } + }; + + class SettingsObserver extends ContentObserver { + SettingsObserver(Handler handler) { + super(handler); + } + + void observe() { + ContentResolver resolver = mContext.getContentResolver(); + resolver.registerContentObserver(Settings.System + .getUriFor(Settings.System.NETWORK_TRAFFIC_STATE), false, + this, UserHandle.USER_ALL); + resolver.registerContentObserver(Settings.System + .getUriFor(Settings.System.NETWORK_TRAFFIC_AUTOHIDE_THRESHOLD), false, + this, UserHandle.USER_ALL); + } + + /* + * @hide + */ + @Override + public void onChange(boolean selfChange) { + setMode(); + update(); + } + } + + private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + if (action == null) return; + if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION) && mScreenOn) { + update(); + } else if (action.equals(Intent.ACTION_SCREEN_ON)) { + mScreenOn = true; + update(); + } else if (action.equals(Intent.ACTION_SCREEN_OFF)) { + mScreenOn = false; + clearHandlerCallbacks(); + } + } + }; + + private boolean getConnectAvailable() { + NetworkInfo network = (mConnectivityManager != null) ? mConnectivityManager.getActiveNetworkInfo() : null; + return network != null; + } + + protected void update() { + if (mIsEnabled) { + if (mAttached) { + totalRxBytes = TrafficStats.getTotalRxBytes(); + totalTxBytes = TrafficStats.getTotalTxBytes(); + mTrafficHandler.sendEmptyMessage(1); + } + if (mAutoHideThreshold == 0) + makeVisible(); + return; + } + clearHandlerCallbacks(); + setVisibility(View.GONE); + mVisible = false; + } + + protected void setMode() { + ContentResolver resolver = mContext.getContentResolver(); + mIsEnabled = Settings.System.getIntForUser(resolver, + Settings.System.NETWORK_TRAFFIC_STATE, 0, + UserHandle.USER_CURRENT) == 1; + mAutoHideThreshold = Settings.System.getIntForUser(resolver, + Settings.System.NETWORK_TRAFFIC_AUTOHIDE_THRESHOLD, 1, + UserHandle.USER_CURRENT); + setGravity(Gravity.CENTER); + setMaxLines(2); + setSpacingAndFonts(); + updateTrafficDrawable(); + setWidth(mWidth); + } + + private void clearHandlerCallbacks() { + mTrafficHandler.removeCallbacks(mRunnable); + mTrafficHandler.removeMessages(0); + mTrafficHandler.removeMessages(1); + } + + protected void updateTrafficDrawable() { + setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0); + setTextColor(mTintColor); + } + + protected void setSpacingAndFonts() { + setTextAppearance(R.style.TextAppearance_QS_Status); + setLineSpacing(0.88f, 0.88f); + } + + public void onDensityOrFontScaleChanged() { + setSpacingAndFonts(); + update(); + } + + public void setTintColor(int color) { + mTintColor = color; + updateTrafficDrawable(); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkTrafficSB.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkTrafficSB.java new file mode 100644 index 000000000000..2604c67e472a --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkTrafficSB.java @@ -0,0 +1,158 @@ +package com.android.systemui.statusbar.policy; + +import static com.android.systemui.statusbar.StatusBarIconView.STATE_DOT; +import static com.android.systemui.statusbar.StatusBarIconView.STATE_HIDDEN; +import static com.android.systemui.statusbar.StatusBarIconView.STATE_ICON; + +import android.content.Context; +import android.graphics.Rect; +import android.graphics.Typeface; +import android.text.style.RelativeSizeSpan; +import android.util.AttributeSet; +import android.view.View; + +import com.android.systemui.Dependency; +import com.android.systemui.R; +import com.android.systemui.plugins.DarkIconDispatcher; +import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver; +import com.android.systemui.statusbar.StatusIconDisplayable; + +import java.util.ArrayList; + +public class NetworkTrafficSB extends NetworkTraffic implements DarkReceiver, StatusIconDisplayable { + + public static final String SLOT = "networktraffic"; + private int mVisibleState = -1; + private boolean mSystemIconVisible = true; + + /* + * @hide + */ + public NetworkTrafficSB(Context context) { + this(context, null); + } + + /* + * @hide + */ + public NetworkTrafficSB(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + /* + * @hide + */ + public NetworkTrafficSB(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + } + + @Override + protected void setMode() { + super.setMode(); + mIsEnabled = mIsEnabled; + } + + @Override + protected void setSpacingAndFonts() { + setTextAppearance(R.style.TextAppearance_QS_Status); + setLineSpacing(0.83f, 0.83f); + } + + @Override + protected RelativeSizeSpan getSpeedRelativeSizeSpan() { + return new RelativeSizeSpan(0.70f); + } + + @Override + protected RelativeSizeSpan getUnitRelativeSizeSpan() { + return new RelativeSizeSpan(0.60f); + } + + @Override + public void onDarkChanged(ArrayList areas, float darkIntensity, int tint) { + if (!mIsEnabled) return; + mTintColor = DarkIconDispatcher.getTint(areas, this, tint); + setTextColor(mTintColor); + updateTrafficDrawable(); + } + + @Override + public String getSlot() { + return SLOT; + } + + @Override + public boolean isIconVisible() { + return mIsEnabled; + } + + @Override + public int getVisibleState() { + return mVisibleState; + } + + @Override + public void setVisibleState(int state, boolean mIsEnabled) { + if (state == mVisibleState) { + return; + } + mVisibleState = state; + + switch (state) { + case STATE_ICON: + mSystemIconVisible = true; + break; + case STATE_DOT: + case STATE_HIDDEN: + default: + mSystemIconVisible = false; + break; + } + update(); + } + + @Override + protected void makeVisible() { + boolean show = mSystemIconVisible; + setVisibility(show ? View.VISIBLE + : View.GONE); + mVisible = show; + } + + @Override + public void setStaticDrawableColor(int color) { + mTintColor = color; + setTextColor(mTintColor); + updateTrafficDrawable(); + } + + @Override + public void setDecorColor(int color) { + setTintColor(color); + } + + private void maybeRestoreVisibility() { + if (!mVisible && mIsEnabled && mSystemIconVisible + && restoreViewQuickly()) { + setVisibility(View.VISIBLE); + mVisible = true; + // then let the traffic handler do its checks + update(); + } + } + + public void setTintColor(int color) { + mTintColor = color; + updateTrafficDrawable(); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java index ba39367e290e..440cac32a815 100644 --- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java +++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java @@ -106,6 +106,27 @@ public class ThemeOverlayApplier implements Dumpable { @VisibleForTesting static final String OVERLAY_CATEGORY_ICON_THEME_PICKER = "android.theme.customization.icon_pack.themepicker"; + @VisibleForTesting + static final String OVERLAY_CATEGORY_ICON_SIGNAL = + "android.theme.customization.signal_icon"; + @VisibleForTesting + static final String OVERLAY_CATEGORY_ICON_WIFI = + "android.theme.customization.wifi_icon"; + @VisibleForTesting + static final String OVERLAY_CATEGORY_NAVBAR = + "android.theme.customization.navbar"; + @VisibleForTesting + static final String OVERLAY_CATEGORY_UI_STYLE_ANDROID = + "android.theme.customization.style.android"; + @VisibleForTesting + static final String OVERLAY_CATEGORY_UI_STYLE_SETTINGS = + "android.theme.customization.style.settings"; + @VisibleForTesting + static final String OVERLAY_CATEGORY_UI_STYLE_SYSUI = + "android.theme.customization.style.systemui"; + @VisibleForTesting + static final String OVERLAY_CATEGORY_LOCK_CLOCK_FONT = + "android.theme.customization.lockscreen_clock_font"; /* * All theme customization categories used by the system, in order that they should be applied, @@ -117,10 +138,17 @@ public class ThemeOverlayApplier implements Dumpable { OVERLAY_CATEGORY_SHAPE, OVERLAY_CATEGORY_FONT, OVERLAY_CATEGORY_ACCENT_COLOR, + OVERLAY_CATEGORY_UI_STYLE_ANDROID, + OVERLAY_CATEGORY_UI_STYLE_SETTINGS, + OVERLAY_CATEGORY_UI_STYLE_SYSUI, OVERLAY_CATEGORY_ICON_ANDROID, OVERLAY_CATEGORY_ICON_SYSUI, OVERLAY_CATEGORY_ICON_SETTINGS, - OVERLAY_CATEGORY_ICON_THEME_PICKER); + OVERLAY_CATEGORY_ICON_THEME_PICKER, + OVERLAY_CATEGORY_ICON_SIGNAL, + OVERLAY_CATEGORY_ICON_WIFI, + OVERLAY_CATEGORY_NAVBAR, + OVERLAY_CATEGORY_LOCK_CLOCK_FONT); /* Categories that need to be applied to the current user as well as the system user. */ @VisibleForTesting @@ -129,8 +157,11 @@ public class ThemeOverlayApplier implements Dumpable { OVERLAY_CATEGORY_ACCENT_COLOR, OVERLAY_CATEGORY_FONT, OVERLAY_CATEGORY_SHAPE, + OVERLAY_CATEGORY_UI_STYLE_ANDROID, OVERLAY_CATEGORY_ICON_ANDROID, - OVERLAY_CATEGORY_ICON_SYSUI); + OVERLAY_CATEGORY_ICON_SYSUI, + OVERLAY_CATEGORY_NAVBAR, + OVERLAY_CATEGORY_LOCK_CLOCK_FONT); /* Allowed overlay categories for each target package. */ private final Map> mTargetPackageToCategories = new ArrayMap<>(); @@ -166,11 +197,18 @@ public ThemeOverlayApplier(OverlayManager overlayManager, mCategoryToTargetPackage.put(OVERLAY_CATEGORY_ACCENT_COLOR, ANDROID_PACKAGE); mCategoryToTargetPackage.put(OVERLAY_CATEGORY_FONT, ANDROID_PACKAGE); mCategoryToTargetPackage.put(OVERLAY_CATEGORY_SHAPE, ANDROID_PACKAGE); + mCategoryToTargetPackage.put(OVERLAY_CATEGORY_UI_STYLE_ANDROID, ANDROID_PACKAGE); + mCategoryToTargetPackage.put(OVERLAY_CATEGORY_LOCK_CLOCK_FONT, ANDROID_PACKAGE); + mCategoryToTargetPackage.put(OVERLAY_CATEGORY_UI_STYLE_SETTINGS, SETTINGS_PACKAGE); + mCategoryToTargetPackage.put(OVERLAY_CATEGORY_UI_STYLE_SYSUI, SYSUI_PACKAGE); mCategoryToTargetPackage.put(OVERLAY_CATEGORY_ICON_ANDROID, ANDROID_PACKAGE); mCategoryToTargetPackage.put(OVERLAY_CATEGORY_ICON_SYSUI, SYSUI_PACKAGE); mCategoryToTargetPackage.put(OVERLAY_CATEGORY_ICON_SETTINGS, SETTINGS_PACKAGE); mCategoryToTargetPackage.put(OVERLAY_CATEGORY_ICON_LAUNCHER, mLauncherPackage); mCategoryToTargetPackage.put(OVERLAY_CATEGORY_ICON_THEME_PICKER, mThemePickerPackage); + mCategoryToTargetPackage.put(OVERLAY_CATEGORY_ICON_SIGNAL, SYSUI_PACKAGE); + mCategoryToTargetPackage.put(OVERLAY_CATEGORY_ICON_WIFI, SYSUI_PACKAGE); + mCategoryToTargetPackage.put(OVERLAY_CATEGORY_NAVBAR, SYSUI_PACKAGE); dumpManager.registerDumpable(TAG, this); } @@ -209,8 +247,12 @@ public void applyCurrentUserOverlays( HashSet identifiersPending = new HashSet<>(); if (pendingCreation != null) { for (FabricatedOverlay overlay : pendingCreation) { - identifiersPending.add(overlay.getIdentifier()); - transaction.registerFabricatedOverlay(overlay); + try { + identifiersPending.add(overlay.getIdentifier()); + transaction.registerFabricatedOverlay(overlay); + } catch (NullPointerException e) { + Log.e(TAG, "NPE for overlay.getIdentifier()", e); + } } } diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java index adef1823d491..bc32a5607c59 100644 --- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java +++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java @@ -42,6 +42,7 @@ import android.database.ContentObserver; import android.graphics.Color; import android.net.Uri; +import android.os.SystemProperties; import android.os.Handler; import android.os.UserHandle; import android.os.UserManager; @@ -73,11 +74,15 @@ import com.android.systemui.settings.UserTracker; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener; +import com.android.systemui.statusbar.policy.ConfigurationController; +import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener; import com.android.systemui.util.settings.SecureSettings; import org.json.JSONException; import org.json.JSONObject; +import ink.kaleidoscope.ParallelSpaceManager; + import java.io.PrintWriter; import java.util.Arrays; import java.util.Collection; @@ -140,12 +145,20 @@ public class ThemeOverlayController extends CoreStartable implements Dumpable { private final SparseArray mDeferredWallpaperColors = new SparseArray<>(); private final SparseIntArray mDeferredWallpaperColorsFlags = new SparseIntArray(); private final WakefulnessLifecycle mWakefulnessLifecycle; + private ConfigurationController mConfigurationController; // Defers changing themes until Setup Wizard is done. private boolean mDeferredThemeEvaluation; // Determines if we should ignore THEME_CUSTOMIZATION_OVERLAY_PACKAGES setting changes. private boolean mSkipSettingChange; + private final ConfigurationListener mConfigurationListener = new ConfigurationListener() { + @Override + public void onThemeChanged() { + setBootColorProps(); + } + }; + private final DeviceProvisionedListener mDeviceProvisionedListener = new DeviceProvisionedListener() { @Override @@ -329,7 +342,9 @@ public void onReceive(Context context, Intent intent) { boolean newWorkProfile = Intent.ACTION_MANAGED_PROFILE_ADDED.equals(intent.getAction()); boolean isManagedProfile = mUserManager.isManagedProfile( intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0)); - if (newWorkProfile) { + boolean isParallelSpace = + Intent.ACTION_PARALLEL_SPACE_CHANGED.equals(intent.getAction()); + if (newWorkProfile || isParallelSpace) { if (!mDeviceProvisionedController.isCurrentUserSetup() && isManagedProfile) { Log.i(TAG, "User setup not finished when " + intent.getAction() + " was received. Deferring... Managed profile? " + isManagedProfile); @@ -356,7 +371,8 @@ public ThemeOverlayController(Context context, BroadcastDispatcher broadcastDisp SecureSettings secureSettings, WallpaperManager wallpaperManager, UserManager userManager, DeviceProvisionedController deviceProvisionedController, UserTracker userTracker, DumpManager dumpManager, FeatureFlags featureFlags, - @Main Resources resources, WakefulnessLifecycle wakefulnessLifecycle) { + @Main Resources resources, WakefulnessLifecycle wakefulnessLifecycle, + ConfigurationController configurationController) { super(context); mIsMonetEnabled = featureFlags.isEnabled(Flags.MONET); @@ -372,6 +388,8 @@ public ThemeOverlayController(Context context, BroadcastDispatcher broadcastDisp mUserTracker = userTracker; mResources = resources; mWakefulnessLifecycle = wakefulnessLifecycle; + mConfigurationController = configurationController; + mConfigurationController.addCallback(mConfigurationListener); dumpManager.registerDumpable(TAG, this); } @@ -380,6 +398,7 @@ public void start() { if (DEBUG) Log.d(TAG, "Start"); final IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED); + filter.addAction(Intent.ACTION_PARALLEL_SPACE_CHANGED); filter.addAction(Intent.ACTION_WALLPAPER_CHANGED); mBroadcastDispatcher.registerReceiver(mBroadcastReceiver, filter, mMainExecutor, UserHandle.ALL); @@ -409,9 +428,26 @@ public void onChange(boolean selfChange, Collection collection, int flags, }, UserHandle.USER_ALL); - if (!mIsMonetEnabled) { - return; - } + mSecureSettings.registerContentObserverForUser( + Settings.Secure.getUriFor(Settings.Secure.QS_BRIGHTNESS_SLIDER_POSITION), + false, + new ContentObserver(mBgHandler) { + @Override + public void onChange(boolean selfChange, Collection collection, int flags, + int userId) { + if (DEBUG) Log.d(TAG, "Overlay changed for user: " + userId); + if (mUserTracker.getUserId() != userId) { + return; + } + if (!mDeviceProvisionedController.isUserSetup(userId)) { + Log.i(TAG, "Theme application deferred when setting changed."); + mDeferredThemeEvaluation = true; + return; + } + reevaluateSystemTheme(true /* forceReload */); + } + }, + UserHandle.USER_ALL); mUserTracker.addCallback(mUserTrackerCallback, mMainExecutor); @@ -462,6 +498,25 @@ public void onFinishedGoingToSleep() { } } }); + + // To set props without needing an overlay change, Usually the props are only set when you first change wallpaper i.e after overlay change. + // we wish to avoid this, call setBootColorProps at the start of service, this will set props on boot so by the time you first reboot, + // boot colors would already be there and bootanim would be colored. + setBootColorProps(); + } + + private void setBootColorProps() { + // persist.bootanim.color1, persist.bootanim.color2, persist.bootanim.color3, persist.bootanim.color4 + int[] bootColors = {android.R.color.system_accent3_100, android.R.color.system_accent1_300, android.R.color.system_accent2_500, android.R.color.system_accent1_100}; + try { + for (int i = 0; i < bootColors.length; i++) { + String color = String.valueOf(mResources.getColor(bootColors[i])); + SystemProperties.set(String.format("persist.bootanim.color%d", i + 1), color); + Log.d("ThemeOverlayController", String.format("Writing boot color boot animation colors: %d %s", i, color)); + } + } catch (RuntimeException e) { + Log.w("ThemeOverlayController", "Cannot set sysprop. Look for 'init' and 'dmesg' logs for more info."); + } } private void reevaluateSystemTheme(boolean forceReload) { @@ -532,6 +587,14 @@ protected int getAccentColor(@NonNull WallpaperColors wallpaperColors) { int l = luminosity - 1; resourceName = "android:color/system_" + name + paletteIndex + "_" + l + "00"; } + // Update our transparent resource + if (resourceName.equals("android:color/system_accent1_50")) { + overlay.setResourceValue("android:color/system_accent_transparent_background_light", TypedValue.TYPE_INT_COLOR_ARGB8, + ColorUtils.setAlphaComponent(colorShades.get(i), 0xB1)); + } else if (resourceName.equals("android:color/system_accent1_900")) { + overlay.setResourceValue("android:color/system_accent_transparent_background_dark", TypedValue.TYPE_INT_COLOR_ARGB8, + ColorUtils.setAlphaComponent(colorShades.get(i), 0xAE)); + } overlay.setResourceValue(resourceName, TypedValue.TYPE_INT_COLOR_ARGB8, ColorUtils.setAlphaComponent(colorShades.get(i), 0xFF)); } @@ -635,12 +698,7 @@ private void updateThemeOverlays() { managedProfiles.add(userInfo.getUserHandle()); } } - - if (colorSchemeIsApplied(managedProfiles)) { - Log.d(TAG, "Skipping overlay creation. Theme was already: " + mColorScheme); - return; - } - + managedProfiles.addAll(ParallelSpaceManager.getInstance().getParallelUserHandles()); if (DEBUG) { Log.d(TAG, "Applying overlays: " + categoryToPackage.keySet().stream() .map(key -> key + " -> " + categoryToPackage.get(key)).collect( diff --git a/packages/SystemUI/src/com/android/systemui/tristate/TriStateUiController.java b/packages/SystemUI/src/com/android/systemui/tristate/TriStateUiController.java new file mode 100644 index 000000000000..bc9c438dd37b --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/tristate/TriStateUiController.java @@ -0,0 +1,32 @@ +/* + * Copyright 2019 CypherOS + * Copyright 2014-2020 Paranoid Android + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.tristate; + +import com.android.systemui.plugins.Plugin; +import com.android.systemui.plugins.VolumeDialog.Callback; +import com.android.systemui.plugins.annotations.DependsOn; +import com.android.systemui.plugins.annotations.ProvidesInterface; + +@DependsOn(target = Callback.class) +@ProvidesInterface(action = "com.android.systemui.action.PLUGIN_TRI_STATE_UI", version = 1) +public interface TriStateUiController extends Plugin { + + public interface UserActivityListener { + void onTriStateUserActivity(); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/tristate/TriStateUiControllerImpl.java b/packages/SystemUI/src/com/android/systemui/tristate/TriStateUiControllerImpl.java new file mode 100644 index 000000000000..7435586e7cfa --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/tristate/TriStateUiControllerImpl.java @@ -0,0 +1,486 @@ +/* + * Copyright 2019 CypherOS + * Copyright 2014-2020 Paranoid Android + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.tristate; + +import static android.view.Surface.ROTATION_90; +import static android.view.Surface.ROTATION_180; +import static android.view.Surface.ROTATION_270; + +import android.app.Dialog; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.res.ColorStateList; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.graphics.drawable.ColorDrawable; +import android.hardware.display.DisplayManagerGlobal; +import android.media.AudioManager; +import android.os.Build; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.provider.Settings; +import android.util.DisplayUtils; +import android.util.Log; +import android.view.Display; +import android.view.DisplayInfo; +import android.view.OrientationEventListener; +import android.view.ViewGroup; +import android.view.Window; +import android.view.WindowManager.LayoutParams; +import android.widget.ImageView; +import android.widget.TextView; + +import com.android.systemui.Dependency; +import com.android.systemui.R; +import com.android.systemui.tristate.TriStateUiController; +import com.android.systemui.tristate.TriStateUiController.UserActivityListener; +import com.android.systemui.plugins.VolumeDialogController; +import com.android.systemui.plugins.VolumeDialogController.Callbacks; +import com.android.systemui.plugins.VolumeDialogController.State; +import com.android.systemui.statusbar.policy.ConfigurationController; +import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener; + +public class TriStateUiControllerImpl implements ConfigurationListener, TriStateUiController { + + private static String TAG = "TriStateUiControllerImpl"; + + private static final int MSG_DIALOG_SHOW = 1; + private static final int MSG_DIALOG_DISMISS = 2; + private static final int MSG_RESET_SCHEDULE = 3; + private static final int MSG_STATE_CHANGE = 4; + + private static final int MODE_NORMAL = AudioManager.RINGER_MODE_NORMAL; + private static final int MODE_SILENT = AudioManager.RINGER_MODE_SILENT; + private static final int MODE_VIBRATE = AudioManager.RINGER_MODE_VIBRATE; + + private static final int TRI_STATE_UI_POSITION_LEFT = 0; + private static final int TRI_STATE_UI_POSITION_RIGHT = 1; + + private static final int DIALOG_TIMEOUT = 2000; + + private Context mContext; + private final VolumeDialogController mVolumeDialogController; + private final Callbacks mVolumeDialogCallback = new Callbacks() { + @Override + public void onShowRequested(int reason, boolean keyguardLocked, int lockTaskModeState) { } + + @Override + public void onDismissRequested(int reason) { } + + @Override + public void onScreenOff() { } + + @Override + public void onStateChanged(State state) { } + + @Override + public void onLayoutDirectionChanged(int layoutDirection) { } + + @Override + public void onShowVibrateHint() { } + + @Override + public void onShowSilentHint() { } + + @Override + public void onShowSafetyWarning(int flags) { } + + @Override + public void onAccessibilityModeChanged(Boolean showA11yStream) { } + + @Override + public void onCaptionComponentStateChanged( + Boolean isComponentEnabled, Boolean fromTooltip) {} + + @Override + public void onConfigurationChanged() { + updateTheme(); + updateTriStateLayout(); + } + }; + + private int mDensity; + private Dialog mDialog; + private int mDialogPosition; + private ViewGroup mDialogView; + private final H mHandler; + private UserActivityListener mListener; + OrientationEventListener mOrientationListener; + private int mOrientationType = 0; + private boolean mShowing = false; + private int mBackgroundColor = 0; + private int mThemeMode = 0; + private int mIconColor = 0; + private int mTextColor = 0; + private ImageView mTriStateIcon; + private TextView mTriStateText; + private int mTriStateMode = -1; + private Window mWindow; + private LayoutParams mWindowLayoutParams; + private int mWindowType; + + private final BroadcastReceiver mRingerStateReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + updateRingerModeChanged(); + } + }; + + private final class H extends Handler { + private TriStateUiControllerImpl mUiController; + + public H(TriStateUiControllerImpl uiController) { + super(Looper.getMainLooper()); + mUiController = uiController; + } + + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_DIALOG_SHOW: + mUiController.handleShow(); + return; + case MSG_DIALOG_DISMISS: + mUiController.handleDismiss(); + return; + case MSG_RESET_SCHEDULE: + mUiController.handleResetTimeout(); + return; + case MSG_STATE_CHANGE: + mUiController.handleStateChanged(); + return; + default: + return; + } + } + } + + public TriStateUiControllerImpl(Context context) { + mContext = context; + mHandler = new H(this); + mOrientationListener = new OrientationEventListener(mContext, 3) { + @Override + public void onOrientationChanged(int orientation) { + checkOrientationType(); + } + }; + mVolumeDialogController = (VolumeDialogController) Dependency.get(VolumeDialogController.class); + IntentFilter ringerChanged = new IntentFilter(AudioManager.RINGER_MODE_CHANGED_ACTION); + mContext.registerReceiver(mRingerStateReceiver, ringerChanged); + } + + private void checkOrientationType() { + Display display = DisplayManagerGlobal.getInstance().getRealDisplay(0); + if (display != null) { + int rotation = display.getRotation(); + if (rotation != mOrientationType) { + mOrientationType = rotation; + updateTriStateLayout(); + } + } + } + + public void init(int windowType, UserActivityListener listener) { + mWindowType = windowType; + mDensity = mContext.getResources().getConfiguration().densityDpi; + mListener = listener; + ((ConfigurationController) Dependency.get(ConfigurationController.class)).addCallback(this); + mVolumeDialogController.addCallback(mVolumeDialogCallback, mHandler); + initDialog(); + } + + public void destroy() { + ((ConfigurationController) Dependency.get(ConfigurationController.class)).removeCallback(this); + mVolumeDialogController.removeCallback(mVolumeDialogCallback); + mContext.unregisterReceiver(mRingerStateReceiver); + } + + private void initDialog() { + mDialog = new Dialog(mContext); + mShowing = false; + mWindow = mDialog.getWindow(); + mWindow.requestFeature(Window.FEATURE_NO_TITLE); + mWindow.setBackgroundDrawable(new ColorDrawable(0)); + mWindow.clearFlags(LayoutParams.FLAG_DIM_BEHIND); + mWindow.addFlags(LayoutParams.FLAG_NOT_FOCUSABLE + | LayoutParams.FLAG_LAYOUT_IN_SCREEN + | LayoutParams.FLAG_NOT_TOUCH_MODAL + | LayoutParams.FLAG_SHOW_WHEN_LOCKED + | LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH + | LayoutParams.FLAG_HARDWARE_ACCELERATED); + mDialog.setCanceledOnTouchOutside(false); + mWindowLayoutParams = mWindow.getAttributes(); + mWindowLayoutParams.type = mWindowType; + mWindowLayoutParams.format = -3; + mWindowLayoutParams.setTitle(TriStateUiControllerImpl.class.getSimpleName()); + mWindowLayoutParams.gravity = 53; + mWindowLayoutParams.y = mDialogPosition; + mWindow.setAttributes(mWindowLayoutParams); + mWindow.setSoftInputMode(LayoutParams.SOFT_INPUT_ADJUST_NOTHING); + mDialog.setContentView(R.layout.tri_state_dialog); + mDialogView = (ViewGroup) mDialog.findViewById(R.id.tri_state_layout); + mTriStateIcon = (ImageView) mDialog.findViewById(R.id.tri_state_icon); + mTriStateText = (TextView) mDialog.findViewById(R.id.tri_state_text); + updateTheme(); + } + + public void show() { + mHandler.obtainMessage(MSG_DIALOG_SHOW, 0, 0).sendToTarget(); + } + + private void registerOrientationListener(boolean enable) { + if (mOrientationListener.canDetectOrientation() && enable) { + Log.v(TAG, "Can detect orientation"); + mOrientationListener.enable(); + return; + } + Log.v(TAG, "Cannot detect orientation"); + mOrientationListener.disable(); + } + + private void updateTriStateLayout() { + if (mContext != null) { + DisplayInfo displayInfo = new DisplayInfo(); + mContext.getDisplay().getDisplayInfo(displayInfo); + final Display.Mode maxDisplayMode = + DisplayUtils.getMaximumResolutionDisplayMode(displayInfo.supportedModes); + final float scaleFactor = DisplayUtils.getPhysicalPixelDisplaySizeRatio( + maxDisplayMode.getPhysicalWidth(), maxDisplayMode.getPhysicalHeight(), + displayInfo.getNaturalWidth(), displayInfo.getNaturalHeight()); + + int iconId = 0; + int textId = 0; + int bg = 0; + Resources res = mContext.getResources(); + if (res != null) { + int positionY; + int positionY2 = mWindowLayoutParams.y; + int positionX = mWindowLayoutParams.x; + int gravity = mWindowLayoutParams.gravity; + switch (mTriStateMode) { + case MODE_SILENT: + iconId = R.drawable.ic_volume_ringer_mute; + textId = R.string.volume_ringer_status_silent; + break; + case MODE_VIBRATE: + iconId = R.drawable.ic_volume_ringer_vibrate; + textId = R.string.volume_ringer_status_vibrate; + break; + case MODE_NORMAL: + iconId = R.drawable.ic_volume_ringer; + textId = R.string.volume_ringer_status_normal; + break; + } + int triStatePos = res.getInteger(com.android.internal.R.integer.config_alertSliderLocation); + boolean isTsKeyRight = true; + if (triStatePos == TRI_STATE_UI_POSITION_LEFT) { + isTsKeyRight = false; + } else if (triStatePos == TRI_STATE_UI_POSITION_RIGHT) { + isTsKeyRight = true; + } + switch (mOrientationType) { + case ROTATION_90: + if (isTsKeyRight) { + gravity = 51; + } else { + gravity = 83; + } + positionY2 = res.getDimensionPixelSize(R.dimen.tri_state_up_dialog_position_deep_land); + if (isTsKeyRight) { + positionY2 += res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height); + } + if (mTriStateMode == MODE_SILENT) { + positionX = res.getDimensionPixelSize(R.dimen.tri_state_up_dialog_position_l); + } else if (mTriStateMode == MODE_VIBRATE) { + positionX = res.getDimensionPixelSize(R.dimen.tri_state_middle_dialog_position_l); + } else if (mTriStateMode == MODE_NORMAL) { + positionX = res.getDimensionPixelSize(R.dimen.tri_state_down_dialog_position_l); + } + bg = R.drawable.dialog_tri_state_middle_bg; + break; + case ROTATION_180: + if (isTsKeyRight) { + gravity = 83; + } else { + gravity = 85; + } + positionX = res.getDimensionPixelSize(R.dimen.tri_state_up_dialog_position_deep); + if (mTriStateMode != MODE_SILENT) { + if (mTriStateMode != MODE_VIBRATE) { + if (mTriStateMode == MODE_NORMAL) { + positionY = res.getDimensionPixelSize(R.dimen.tri_state_down_dialog_position) + res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height); + } + bg = R.drawable.dialog_tri_state_middle_bg; + break; + } + positionY = res.getDimensionPixelSize(R.dimen.tri_state_middle_dialog_position) + res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height); + } else { + positionY = res.getDimensionPixelSize(R.dimen.tri_state_up_dialog_position) + res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height); + } + positionY2 = positionY; + bg = R.drawable.dialog_tri_state_middle_bg; + case ROTATION_270: + if (isTsKeyRight) { + gravity = 85; + } else { + gravity = 53; + } + positionY2 = res.getDimensionPixelSize(R.dimen.tri_state_up_dialog_position_deep_land); + if (!isTsKeyRight) { + positionY2 += res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height); + } + if (mTriStateMode == MODE_SILENT) { + positionX = res.getDimensionPixelSize(R.dimen.tri_state_up_dialog_position_l); + } else if (mTriStateMode == MODE_VIBRATE) { + positionX = res.getDimensionPixelSize(R.dimen.tri_state_middle_dialog_position_l); + } else if (mTriStateMode == MODE_NORMAL) { + positionX = res.getDimensionPixelSize(R.dimen.tri_state_down_dialog_position_l); + } + bg = R.drawable.dialog_tri_state_middle_bg; + break; + default: + if (isTsKeyRight) { + gravity = 53; + } else { + gravity = 51; + } + positionX = res.getDimensionPixelSize(R.dimen.tri_state_up_dialog_position_deep); + if (mTriStateMode != MODE_SILENT) { + if (mTriStateMode != MODE_VIBRATE) { + if (mTriStateMode == MODE_NORMAL) { + positionY2 = res.getDimensionPixelSize(R.dimen.tri_state_down_dialog_position) + res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height); + bg = R.drawable.dialog_tri_state_down_bg; + break; + } + } + positionY2 = res.getDimensionPixelSize(R.dimen.tri_state_middle_dialog_position) + res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height); + bg = R.drawable.dialog_tri_state_middle_bg; + break; + } + positionY2 = res.getDimensionPixelSize(R.dimen.tri_state_up_dialog_position) + res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height); + bg = R.drawable.dialog_tri_state_up_bg; + break; + } + if (mTriStateMode != -1) { + if (mTriStateIcon != null) { + mTriStateIcon.setImageResource(iconId); + } + if (mTriStateText != null) { + String inputText = res.getString(textId); + if (inputText != null && mTriStateText.length() == inputText.length()) { + StringBuilder sb = new StringBuilder(); + sb.append(inputText); + sb.append(" "); + inputText = sb.toString(); + } + mTriStateText.setText(inputText); + } + if (mDialogView != null) { + mDialogView.setBackgroundDrawable(res.getDrawable(bg)); + } + mDialogPosition = positionY2; + } + positionY = res.getDimensionPixelSize(R.dimen.tri_state_dialog_padding); + mWindowLayoutParams.gravity = gravity; + mWindowLayoutParams.y = (int) ((positionY2 - positionY) * scaleFactor); + mWindowLayoutParams.x = (int) ((positionX - positionY) * scaleFactor); + mWindow.setAttributes(mWindowLayoutParams); + handleResetTimeout(); + } + } + } + + private void updateRingerModeChanged() { + mHandler.obtainMessage(MSG_STATE_CHANGE, 0, 0).sendToTarget(); + if (mTriStateMode != -1) { + show(); + } + } + + private void handleShow() { + mHandler.removeMessages(MSG_DIALOG_SHOW); + mHandler.removeMessages(MSG_DIALOG_DISMISS); + handleResetTimeout(); + if (!mShowing) { + updateTheme(); + registerOrientationListener(true); + checkOrientationType(); + mShowing = true; + mDialog.show(); + if (mListener != null) { + mListener.onTriStateUserActivity(); + } + } + } + + private void handleDismiss() { + mHandler.removeMessages(MSG_DIALOG_SHOW); + mHandler.removeMessages(MSG_DIALOG_DISMISS); + if (mShowing) { + registerOrientationListener(false); + mShowing = false; + mDialog.dismiss(); + } + } + + private void handleStateChanged() { + AudioManager am = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); + int ringerMode = am.getRingerModeInternal(); + if (ringerMode != mTriStateMode) { + mTriStateMode = ringerMode; + updateTriStateLayout(); + if (mListener != null) { + mListener.onTriStateUserActivity(); + } + } + } + + public void handleResetTimeout() { + mHandler.removeMessages(MSG_DIALOG_DISMISS); + mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_DIALOG_DISMISS, MSG_RESET_SCHEDULE, 0), (long) DIALOG_TIMEOUT); + if (mListener != null) { + mListener.onTriStateUserActivity(); + } + } + + @Override + public void onDensityOrFontScaleChanged() { + handleDismiss(); + initDialog(); + updateTriStateLayout(); + } + + private void updateTheme() { + // Todo: Add some logic to update the theme only when a new theme is applied + mIconColor = getAttrColor(android.R.attr.colorAccent); + mTextColor = getAttrColor(android.R.attr.textColorPrimary); + mBackgroundColor = getAttrColor(android.R.attr.colorPrimary); + mDialogView.setBackgroundTintList(ColorStateList.valueOf(mBackgroundColor)); + mTriStateIcon.setColorFilter(mIconColor); + mTriStateText.setTextColor(mTextColor); + } + + public int getAttrColor(int attr) { + TypedArray ta = mContext.obtainStyledAttributes(new int[]{attr}); + int colorAccent = ta.getColor(0, 0); + ta.recycle(); + return colorAccent; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/tuner/ClockPreference.java b/packages/SystemUI/src/com/android/systemui/tuner/ClockPreference.java deleted file mode 100644 index c92d7bbfe0b7..000000000000 --- a/packages/SystemUI/src/com/android/systemui/tuner/ClockPreference.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file - * except in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the - * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ -package com.android.systemui.tuner; - -import android.content.Context; -import android.text.TextUtils; -import android.util.ArraySet; -import android.util.AttributeSet; - -import androidx.preference.DropDownPreference; - -import com.android.systemui.Dependency; -import com.android.systemui.statusbar.phone.StatusBarIconController; -import com.android.systemui.statusbar.policy.Clock; - -public class ClockPreference extends DropDownPreference implements TunerService.Tunable { - - private static final String SECONDS = "seconds"; - private static final String DEFAULT = "default"; - private static final String DISABLED = "disabled"; - - private final String mClock; - private boolean mClockEnabled; - private boolean mHasSeconds; - private ArraySet mHideList; - private boolean mHasSetValue; - private boolean mReceivedSeconds; - private boolean mReceivedClock; - - public ClockPreference(Context context, AttributeSet attrs) { - super(context, attrs); - mClock = context.getString(com.android.internal.R.string.status_bar_clock); - setEntryValues(new CharSequence[] { SECONDS, DEFAULT, DISABLED }); - } - - @Override - public void onAttached() { - super.onAttached(); - Dependency.get(TunerService.class).addTunable(this, StatusBarIconController.ICON_HIDE_LIST, - Clock.CLOCK_SECONDS); - } - - @Override - public void onDetached() { - Dependency.get(TunerService.class).removeTunable(this); - super.onDetached(); - } - - @Override - public void onTuningChanged(String key, String newValue) { - if (StatusBarIconController.ICON_HIDE_LIST.equals(key)) { - mReceivedClock = true; - mHideList = StatusBarIconController.getIconHideList(getContext(), newValue); - mClockEnabled = !mHideList.contains(mClock); - } else if (Clock.CLOCK_SECONDS.equals(key)) { - mReceivedSeconds = true; - mHasSeconds = newValue != null && Integer.parseInt(newValue) != 0; - } - if (!mHasSetValue && mReceivedClock && mReceivedSeconds) { - // Because of the complicated tri-state it can end up looping and setting state back to - // what the user didn't choose. To avoid this, just set the state once and rely on the - // preference to handle updates. - mHasSetValue = true; - if (mClockEnabled && mHasSeconds) { - setValue(SECONDS); - } else if (mClockEnabled) { - setValue(DEFAULT); - } else { - setValue(DISABLED); - } - } - } - - @Override - protected boolean persistString(String value) { - Dependency.get(TunerService.class).setValue(Clock.CLOCK_SECONDS, SECONDS.equals(value) ? 1 - : 0); - if (DISABLED.equals(value)) { - mHideList.add(mClock); - } else { - mHideList.remove(mClock); - } - Dependency.get(TunerService.class).setValue(StatusBarIconController.ICON_HIDE_LIST, - TextUtils.join(",", mHideList)); - return true; - } -} diff --git a/packages/SystemUI/src/com/android/systemui/tuner/DemoModeFragment.java b/packages/SystemUI/src/com/android/systemui/tuner/DemoModeFragment.java index 1f444340653d..54bde250b9e4 100644 --- a/packages/SystemUI/src/com/android/systemui/tuner/DemoModeFragment.java +++ b/packages/SystemUI/src/com/android/systemui/tuner/DemoModeFragment.java @@ -84,11 +84,10 @@ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { mDemoModeTracker.startTracking(); updateDemoModeEnabled(); updateDemoModeOn(); - - setHasOptionsMenu(true); + //setHasOptionsMenu(true); } - @Override + /*@Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case android.R.id.home: @@ -96,7 +95,7 @@ public boolean onOptionsItemSelected(MenuItem item) { break; } return super.onOptionsItemSelected(item); - } + }*/ @Override public void onResume() { @@ -222,4 +221,10 @@ public void onDemoModeFinished() { updateDemoModeOn(); } }; + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + getActivity().getActionBar().setDisplayHomeAsUpEnabled(true); + } } diff --git a/packages/SystemUI/src/com/android/systemui/tuner/StatusBarTuner.java b/packages/SystemUI/src/com/android/systemui/tuner/StatusBarTuner.java new file mode 100644 index 000000000000..91a0a1ed9437 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/tuner/StatusBarTuner.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2017 The LineageOS Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.tuner; + +import android.os.Bundle; +import android.view.MenuItem; + +import androidx.preference.PreferenceFragment; + +import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.nano.MetricsProto.MetricsEvent; +import com.android.systemui.R; + +public class StatusBarTuner extends PreferenceFragment { + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + setHasOptionsMenu(true); + } + + @Override + public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { + addPreferencesFromResource(R.xml.status_bar_prefs); + } + + @Override + public void onResume() { + super.onResume(); + MetricsLogger.visibility(getContext(), MetricsEvent.TUNER, true); + } + + @Override + public void onPause() { + super.onPause(); + MetricsLogger.visibility(getContext(), MetricsEvent.TUNER, false); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (item.getItemId() == android.R.id.home) { + getActivity().onBackPressed(); + return true; + } + return false; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java index 3231aecdc4b2..c0b748832091 100644 --- a/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java +++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java @@ -23,7 +23,6 @@ import android.view.MenuItem; import android.view.Window; import android.view.WindowManager; -import android.widget.Toolbar; import androidx.preference.Preference; import androidx.preference.PreferenceFragment; @@ -36,7 +35,9 @@ import javax.inject.Inject; -public class TunerActivity extends Activity implements +import com.android.settingslib.collapsingtoolbar.CollapsingToolbarBaseActivity; + +public class TunerActivity extends CollapsingToolbarBaseActivity implements PreferenceFragment.OnPreferenceStartFragmentCallback, PreferenceFragment.OnPreferenceStartScreenCallback { @@ -53,24 +54,23 @@ public class TunerActivity extends Activity implements } protected void onCreate(Bundle savedInstanceState) { + requestWindowFeature(Window.FEATURE_NO_TITLE); super.onCreate(savedInstanceState); - setTheme(R.style.Theme_AppCompat_DayNight); getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); - requestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.tuner_activity); - Toolbar toolbar = findViewById(R.id.action_bar); - if (toolbar != null) { - setActionBar(toolbar); - } if (getFragmentManager().findFragmentByTag(TAG_TUNER) == null) { final String action = getIntent().getAction(); - boolean showDemoMode = action != null && action.equals( - "com.android.settings.action.DEMO_MODE"); - final PreferenceFragment fragment = showDemoMode - ? new DemoModeFragment(mDemoModeController) - : new TunerFragment(mTunerService); + final Fragment fragment; + if ("com.android.settings.action.DEMO_MODE".equals(action)) { + fragment = new DemoModeFragment(mDemoModeController); + } else if ("com.android.settings.action.STATUS_BAR_TUNER".equals(action)) { + fragment = new StatusBarTuner(); + } else { + fragment = new TunerFragment(mTunerService); + } + getFragmentManager().beginTransaction().replace(R.id.content_frame, fragment, TAG_TUNER).commit(); } diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java index 989462a9fd34..29d6d00b6eb4 100644 --- a/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java +++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java @@ -71,12 +71,6 @@ public void onCreate(Bundle savedInstanceState) { setHasOptionsMenu(true); } - @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - getActivity().getActionBar().setDisplayHomeAsUpEnabled(true); - } - @Override public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { addPreferencesFromResource(R.xml.tuner_prefs); @@ -92,13 +86,6 @@ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { if (preference != null) getPreferenceScreen().removePreference(preference); } } - - if (Settings.Secure.getInt(getContext().getContentResolver(), SETTING_SEEN_TUNER_WARNING, - 0) == 0) { - if (getFragmentManager().findFragmentByTag(WARNING_TAG) == null) { - new TunerWarningFragment().show(getFragmentManager(), WARNING_TAG); - } - } } private boolean alwaysOnAvailable() { diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java index 5d09e064604a..b3bd5206cd8e 100644 --- a/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java +++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java @@ -75,4 +75,12 @@ public static boolean parseIntegerSwitch(String value, boolean defaultValue) { return defaultValue; } } + + public static int parseInteger(String value, int defaultValue) { + try { + return value != null ? Integer.parseInt(value) : defaultValue; + } catch (NumberFormatException e) { + return defaultValue; + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java index b23d870fb82f..e7292959c66c 100644 --- a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java +++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java @@ -152,41 +152,85 @@ private void upgradeTuner(int oldVersion, int newVersion, Handler mainHandler) { if (oldVersion < 2) { setTunerEnabled(false); } - // 3 Removed because of a revert. - if (oldVersion < 4) { - // Delay this so that we can wait for everything to be registered first. - final int user = mCurrentUser; - mainHandler.postDelayed( - () -> clearAllFromUser(user), 5000); - } setValue(TUNER_VERSION, newVersion); } + private boolean isSystem(String key) { + return key.startsWith("system:"); + } + + private boolean isGlobal(String key) { + return key.startsWith("global:"); + } + + private String chomp(String key) { + return key.replaceFirst("^(system|global):", ""); + } + @Override public String getValue(String setting) { - return Settings.Secure.getStringForUser(mContentResolver, setting, mCurrentUser); + if (isSystem(setting)) { + return Settings.System.getStringForUser( + mContentResolver, chomp(setting), mCurrentUser); + } else if (isGlobal(setting)) { + return Settings.Global.getStringForUser( + mContentResolver, chomp(setting), mCurrentUser); + } else { + return Settings.Secure.getStringForUser(mContentResolver, setting, mCurrentUser); + } } @Override public void setValue(String setting, String value) { - Settings.Secure.putStringForUser(mContentResolver, setting, value, mCurrentUser); + if (isSystem(setting)) { + Settings.System.putStringForUser( + mContentResolver, chomp(setting), value, mCurrentUser); + } else if (isGlobal(setting)) { + Settings.Global.putStringForUser( + mContentResolver, chomp(setting), value, mCurrentUser); + } else { + Settings.Secure.putStringForUser(mContentResolver, setting, value, mCurrentUser); + } } @Override public int getValue(String setting, int def) { - return Settings.Secure.getIntForUser(mContentResolver, setting, def, mCurrentUser); + if (isSystem(setting)) { + return Settings.System.getIntForUser( + mContentResolver, chomp(setting), def, mCurrentUser); + } else if (isGlobal(setting)) { + return Settings.Global.getInt( + mContentResolver, chomp(setting), def); + } else { + return Settings.Secure.getIntForUser(mContentResolver, setting, def, mCurrentUser); + } } @Override public String getValue(String setting, String def) { - String ret = Secure.getStringForUser(mContentResolver, setting, mCurrentUser); + String ret; + if (isSystem(setting)) { + ret = Settings.System.getStringForUser( + mContentResolver, chomp(setting), mCurrentUser); + } else if (isGlobal(setting)) { + ret = Settings.Global.getStringForUser( + mContentResolver, chomp(setting), mCurrentUser); + } else { + ret = Secure.getStringForUser(mContentResolver, setting, mCurrentUser); + } if (ret == null) return def; return ret; } @Override public void setValue(String setting, int value) { - Settings.Secure.putIntForUser(mContentResolver, setting, value, mCurrentUser); + if (isSystem(setting)) { + Settings.System.putIntForUser(mContentResolver, chomp(setting), value, mCurrentUser); + } else if (isGlobal(setting)) { + Settings.Global.putInt(mContentResolver, chomp(setting), value); + } else { + Settings.Secure.putIntForUser(mContentResolver, setting, value, mCurrentUser); + } } @Override @@ -205,14 +249,20 @@ private void addTunable(Tunable tunable, String key) { mTunables.add(tunable); mLeakDetector.trackCollection(mTunables, "TunerService.mTunables"); } - Uri uri = Settings.Secure.getUriFor(key); + final Uri uri; + if (isSystem(key)) { + uri = Settings.System.getUriFor(chomp(key)); + } else if (isGlobal(key)) { + uri = Settings.Global.getUriFor(chomp(key)); + } else { + uri = Settings.Secure.getUriFor(key); + } if (!mListeningUris.containsKey(uri)) { mListeningUris.put(uri, key); mContentResolver.registerContentObserver(uri, false, mObserver, mCurrentUser); } // Send the first state. - String value = DejankUtils.whitelistIpcs(() -> Settings.Secure - .getStringForUser(mContentResolver, key, mCurrentUser)); + String value = DejankUtils.whitelistIpcs(() -> getValue(key)); tunable.onTuningChanged(key, value); } @@ -242,18 +292,23 @@ private void reloadSetting(Uri uri) { if (tunables == null) { return; } - String value = Settings.Secure.getStringForUser(mContentResolver, key, mCurrentUser); + String value = getValue(key); for (Tunable tunable : tunables) { - tunable.onTuningChanged(key, value); + if (tunable != null) { + tunable.onTuningChanged(key, value); + } } } private void reloadAll() { for (String key : mTunableLookup.keySet()) { - String value = Settings.Secure.getStringForUser(mContentResolver, key, - mCurrentUser); + if (ArrayUtils.contains(RESET_EXCEPTION_LIST, key) || key.startsWith("system:")) + continue; + String value = getValue(key); for (Tunable tunable : mTunableLookup.get(key)) { - tunable.onTuningChanged(key, value); + if (tunable != null) { + tunable.onTuningChanged(key, value); + } } } } @@ -270,9 +325,6 @@ public void clearAllFromUser(int user) { // A couple special cases. for (String key : mTunableLookup.keySet()) { - if (ArrayUtils.contains(RESET_EXCEPTION_LIST, key)) { - continue; - } Settings.Secure.putStringForUser(mContentResolver, key, null, user); } } diff --git a/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java b/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java index 4dc78f9ec8a6..0e5a5fd007fb 100644 --- a/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java +++ b/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java @@ -379,9 +379,8 @@ private Notification onVolumeMounted(VolumeInfo vol) { final VolumeRecord rec = mStorageManager.findRecordByUuid(vol.getFsUuid()); final DiskInfo disk = vol.getDisk(); - // Don't annoy when user dismissed in past. (But make sure the disk is adoptable; we - // used to allow snoozing non-adoptable disks too.) - if (rec.isSnoozed() && disk.isAdoptable()) { + // Don't annoy when user dismissed in past. + if (rec.isSnoozed() && (disk.isAdoptable() || disk.isSd())) { return null; } if (disk.isAdoptable() && !rec.isInited() && rec.getType() != VolumeInfo.TYPE_PUBLIC @@ -424,8 +423,7 @@ private Notification onVolumeMounted(VolumeInfo vol) { buildUnmountPendingIntent(vol))) .setContentIntent(browseIntent) .setCategory(Notification.CATEGORY_SYSTEM); - // Non-adoptable disks can't be snoozed. - if (disk.isAdoptable()) { + if (disk.isAdoptable() || disk.isSd()) { builder.setDeleteIntent(buildSnoozeIntent(vol.getFsUuid())); } diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java index f71d98827e4b..6f2c4b4bad17 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java @@ -26,6 +26,9 @@ import android.view.WindowManager.LayoutParams; import com.android.settingslib.applications.InterestingConfigChanges; +import com.android.systemui.Dependency; +import com.android.systemui.tristate.TriStateUiController; +import com.android.systemui.tristate.TriStateUiControllerImpl; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.demomode.DemoMode; import com.android.systemui.demomode.DemoModeController; @@ -49,7 +52,7 @@ */ @SysUISingleton public class VolumeDialogComponent implements VolumeComponent, TunerService.Tunable, - VolumeDialogControllerImpl.UserActivityListener{ + VolumeDialogControllerImpl.UserActivityListener, TriStateUiController.UserActivityListener { public static final String VOLUME_DOWN_SILENT = "sysui_volume_down_silent"; public static final String VOLUME_UP_SILENT = "sysui_volume_up_silent"; @@ -66,6 +69,7 @@ public class VolumeDialogComponent implements VolumeComponent, TunerService.Tuna protected final Context mContext; private final VolumeDialogControllerImpl mController; + private TriStateUiControllerImpl mTriStateController; private final InterestingConfigChanges mConfigChanges = new InterestingConfigChanges( ActivityInfo.CONFIG_FONT_SCALE | ActivityInfo.CONFIG_LOCALE | ActivityInfo.CONFIG_ASSETS_PATHS | ActivityInfo.CONFIG_UI_MODE); @@ -95,6 +99,8 @@ public VolumeDialogComponent( mActivityStarter = activityStarter; mController = volumeDialogController; mController.setUserActivityListener(this); + boolean hasAlertSlider = mContext.getResources(). + getBoolean(com.android.internal.R.bool.config_hasAlertSlider); // Allow plugins to reference the VolumeDialogController. pluginDependencyProvider.allowPluginDependency(VolumeDialogController.class); extensionController.newExtension(VolumeDialog.class) @@ -106,6 +112,13 @@ public VolumeDialogComponent( } mDialog = dialog; mDialog.init(LayoutParams.TYPE_VOLUME_OVERLAY, mVolumeDialogCallback); + if (hasAlertSlider) { + if (mTriStateController != null) { + mTriStateController.destroy(); + } + mTriStateController = new TriStateUiControllerImpl(mContext); + mTriStateController.init(LayoutParams.TYPE_VOLUME_OVERLAY, this); + } }).build(); applyConfiguration(); tunerService.addTunable(this, VOLUME_DOWN_SILENT, VOLUME_UP_SILENT, @@ -193,6 +206,11 @@ private void startSettings(Intent intent) { mActivityStarter.startActivity(intent, true /* onlyProvisioned */, true /* dismissShade */); } + @Override + public void onTriStateUserActivity() { + onUserActivity(); + } + private final VolumeDialogImpl.Callback mVolumeDialogCallback = new VolumeDialogImpl.Callback() { @Override public void onZenSettingsClicked() { diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java index bf7c4598b5da..6cf8e9333210 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java @@ -54,6 +54,7 @@ import android.util.ArrayMap; import android.util.Log; import android.util.Slog; +import android.view.KeyEvent; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.CaptioningManager; @@ -100,6 +101,7 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION) .build(); + private static final Object ADAPTIVE_PLAYBACK_TOKEN = new Object(); static final ArrayMap STREAMS = new ArrayMap<>(); static { @@ -140,6 +142,11 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa private boolean mShowA11yStream; private boolean mShowVolumeDialog; private boolean mShowSafetyWarning; + + private boolean mAdaptivePlaybackEnabled; + private int mAdaptivePlaybackTimeout; + private boolean mAdaptivePlaybackResumable; + private long mLastToggledRingerOn; private boolean mDeviceInteractive = true; @@ -209,6 +216,11 @@ public VolumeDialogControllerImpl( mActivityManager = activityManager; + mAdaptivePlaybackEnabled = Settings.System.getIntForUser(mContext.getContentResolver(), + Settings.System.ADAPTIVE_PLAYBACK_ENABLED, 0, UserHandle.USER_CURRENT) == 1; + mAdaptivePlaybackTimeout = Settings.System.getIntForUser(mContext.getContentResolver(), + Settings.System.ADAPTIVE_PLAYBACK_TIMEOUT, 30000, UserHandle.USER_CURRENT); + boolean accessibilityVolumeStreamActive = accessibilityManager .isAccessibilityVolumeStreamActive(); mVolumeController.setA11yMode(accessibilityVolumeStreamActive ? @@ -523,6 +535,25 @@ private boolean updateStreamLevelW(int stream, int level) { final StreamState ss = streamStateW(stream); if (ss.level == level) return false; ss.level = level; + if (mAdaptivePlaybackEnabled && stream == AudioSystem.STREAM_MUSIC && level == 0 + && mAudio.isMusicActive()) { + mAudio.dispatchMediaKeyEvent( + new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_PAUSE)); + mAudio.dispatchMediaKeyEvent( + new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MEDIA_PAUSE)); + mAdaptivePlaybackResumable = true; + mWorker.removeCallbacksAndMessages(ADAPTIVE_PLAYBACK_TOKEN); + mWorker.postDelayed(() -> mAdaptivePlaybackResumable = false, ADAPTIVE_PLAYBACK_TOKEN, + mAdaptivePlaybackTimeout); + } + if (stream == AudioSystem.STREAM_MUSIC && level > 0 && mAdaptivePlaybackResumable) { + mWorker.removeCallbacksAndMessages(ADAPTIVE_PLAYBACK_TOKEN); + mAudio.dispatchMediaKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, + KeyEvent.KEYCODE_MEDIA_PLAY)); + mAudio.dispatchMediaKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, + KeyEvent.KEYCODE_MEDIA_PLAY)); + mAdaptivePlaybackResumable = false; + } if (isLogWorthy(stream)) { Events.writeEvent(Events.EVENT_LEVEL_CHANGED, stream, level); } @@ -559,6 +590,16 @@ private static boolean isRinger(int stream) { return stream == AudioManager.STREAM_RING || stream == AudioManager.STREAM_NOTIFICATION; } + private boolean updateLinkNotificationConfigW() { + boolean linkNotificationWithVolume = Settings.Secure.getInt(mContext.getContentResolver(), + Settings.Secure.VOLUME_LINK_NOTIFICATION, 1) == 1; + if (mState.linkedNotification == linkNotificationWithVolume) { + return false; + } + mState.linkedNotification = linkNotificationWithVolume; + return true; + } + private boolean updateEffectsSuppressorW(ComponentName effectsSuppressor) { if (Objects.equals(mState.effectsSuppressor, effectsSuppressor)) return false; mState.effectsSuppressor = effectsSuppressor; @@ -1020,6 +1061,12 @@ private final class SettingObserver extends ContentObserver { Settings.Global.getUriFor(Settings.Global.ZEN_MODE); private final Uri ZEN_MODE_CONFIG_URI = Settings.Global.getUriFor(Settings.Global.ZEN_MODE_CONFIG_ETAG); + private final Uri ADAPTIVE_PLAYBACK_ENABLED_URI = + Settings.System.getUriFor(Settings.System.ADAPTIVE_PLAYBACK_ENABLED); + private final Uri ADAPTIVE_PLAYBACK_TIMEOUT_URI = + Settings.System.getUriFor(Settings.System.ADAPTIVE_PLAYBACK_TIMEOUT); + private final Uri VOLUME_LINK_NOTIFICATION_URI = + Settings.Secure.getUriFor(Settings.Secure.VOLUME_LINK_NOTIFICATION); public SettingObserver(Handler handler) { super(handler); @@ -1028,6 +1075,13 @@ public SettingObserver(Handler handler) { public void init() { mContext.getContentResolver().registerContentObserver(ZEN_MODE_URI, false, this); mContext.getContentResolver().registerContentObserver(ZEN_MODE_CONFIG_URI, false, this); + mContext.getContentResolver().registerContentObserver(ZEN_MODE_CONFIG_URI, false, this); + mContext.getContentResolver().registerContentObserver(ADAPTIVE_PLAYBACK_ENABLED_URI, + false, this, UserHandle.USER_ALL); + mContext.getContentResolver().registerContentObserver(ADAPTIVE_PLAYBACK_TIMEOUT_URI, + false, this, UserHandle.USER_ALL); + mContext.getContentResolver().registerContentObserver(VOLUME_LINK_NOTIFICATION_URI, + false, this); } public void destroy() { @@ -1043,6 +1097,19 @@ public void onChange(boolean selfChange, Uri uri) { if (ZEN_MODE_CONFIG_URI.equals(uri)) { changed |= updateZenConfig(); } + if (ADAPTIVE_PLAYBACK_ENABLED_URI.equals(uri)) { + mAdaptivePlaybackEnabled = Settings.System.getIntForUser( + mContext.getContentResolver(), Settings.System.ADAPTIVE_PLAYBACK_ENABLED, 0, + UserHandle.USER_CURRENT) == 1; + } + if (ADAPTIVE_PLAYBACK_TIMEOUT_URI.equals(uri)) { + mAdaptivePlaybackTimeout = Settings.System.getIntForUser( + mContext.getContentResolver(), Settings.System.ADAPTIVE_PLAYBACK_TIMEOUT, + 30000, UserHandle.USER_CURRENT); + } + if (VOLUME_LINK_NOTIFICATION_URI.equals(uri)) { + changed = updateLinkNotificationConfigW(); + } if (changed) { mCallbacks.onStateChanged(mState); diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java index 3094a8c2eb1b..2551b383f55c 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java @@ -63,6 +63,7 @@ import android.graphics.drawable.Drawable; import android.graphics.drawable.LayerDrawable; import android.graphics.drawable.RotateDrawable; +import android.media.AppVolume; import android.media.AudioManager; import android.media.AudioSystem; import android.os.Debug; @@ -112,6 +113,7 @@ import com.android.internal.jank.InteractionJankMonitor; import com.android.internal.view.RotationPolicy; import com.android.settingslib.Utils; +import com.android.systemui.Dependency; import com.android.systemui.Prefs; import com.android.systemui.R; import com.android.systemui.animation.Interpolators; @@ -237,6 +239,9 @@ public class VolumeDialogImpl implements VolumeDialog, private CaptionsToggleImageButton mODICaptionsIcon; private View mSettingsView; private ImageButton mSettingsIcon; + private View mAppVolumeView; + private ImageButton mAppVolumeIcon; + private String mAppVolumeActivePackageName; private FrameLayout mZenIcon; private final List mRows = new ArrayList<>(); private ConfigurableTexts mConfigurableTexts; @@ -269,11 +274,19 @@ public class VolumeDialogImpl implements VolumeDialog, private ViewStub mODICaptionsTooltipViewStub; private View mODICaptionsTooltipView = null; + // Volume panel placement left or right + private boolean mVolumePanelOnLeft; + private final boolean mUseBackgroundBlur; private Consumer mCrossWindowBlurEnabledListener; private BackgroundBlurDrawable mDialogRowsViewBackground; private final InteractionJankMonitor mInteractionJankMonitor; + // Variable to track the default row with which the panel is initially shown + private VolumeRow mDefaultRow = null; + + private FrameLayout mRoundedBorderBottom; + public VolumeDialogImpl( Context context, VolumeDialogController volumeDialogController, @@ -322,6 +335,10 @@ public VolumeDialogImpl( }; } + if (!mShowActiveStreamOnly) { + mVolumePanelOnLeft = mContext.getResources().getBoolean(R.bool.config_audioPanelOnLeftSide);; + } + initDimens(); } @@ -377,25 +394,36 @@ private void unionViewBoundstoTouchableRegion(final View view) { final int[] locInWindow = new int[2]; view.getLocationInWindow(locInWindow); - float x = locInWindow[0]; - float y = locInWindow[1]; + float xExtraSize = 0; + float yExtraSize = 0; // The ringer and rows container has extra height at the top to fit the expanded ringer // drawer. This area should not be touchable unless the ringer drawer is open. + // In landscape the ringer expands to the left and it has to be ensured that if there + // are multiple rows they are touchable. if (view == mTopContainer && !mIsRingerDrawerOpen) { if (!isLandscape()) { - y += getRingerDrawerOpenExtraSize(); - } else { - x += getRingerDrawerOpenExtraSize(); + yExtraSize = getRingerDrawerOpenExtraSize(); + } else if (getRingerDrawerOpenExtraSize() > getVisibleRowsExtraSize()) { + xExtraSize = (getRingerDrawerOpenExtraSize() - getVisibleRowsExtraSize()); } } - mTouchableRegion.op( - (int) x, - (int) y, - locInWindow[0] + view.getWidth(), - locInWindow[1] + view.getHeight(), - Region.Op.UNION); + if (mVolumePanelOnLeft) { + mTouchableRegion.op( + locInWindow[0], + locInWindow[1] + (int) yExtraSize, + locInWindow[0] + view.getWidth() - (int) xExtraSize, + locInWindow[1] + view.getHeight(), + Region.Op.UNION); + } else { + mTouchableRegion.op( + locInWindow[0] + (int) xExtraSize, + locInWindow[1] + (int) yExtraSize, + locInWindow[0] + view.getWidth(), + locInWindow[1] + view.getHeight(), + Region.Op.UNION); + } } private void initDialog(int lockTaskModeState) { @@ -424,6 +452,10 @@ private void initDialog(int lockTaskModeState) { lp.setTitle(VolumeDialogImpl.class.getSimpleName()); lp.windowAnimations = -1; lp.gravity = mContext.getResources().getInteger(R.integer.volume_dialog_gravity); + if (!mShowActiveStreamOnly) { + lp.gravity &= ~(Gravity.LEFT | Gravity.RIGHT); + lp.gravity |= mVolumePanelOnLeft ? Gravity.LEFT : Gravity.RIGHT; + } mWindow.setAttributes(lp); mWindow.setLayout(WRAP_CONTENT, WRAP_CONTENT); @@ -434,7 +466,8 @@ private void initDialog(int lockTaskModeState) { mDialog.setOnShowListener(dialog -> { mDialogView.getViewTreeObserver().addOnComputeInternalInsetsListener(this); if (!shouldSlideInVolumeTray()) { - mDialogView.setTranslationX(mDialogView.getWidth() / 2.0f); + mDialogView.setTranslationX( + getTranslationForPanelLocation() * mDialogView.getWidth() / 2.0f); } mDialogView.setAlpha(0); mDialogView.animate() @@ -567,6 +600,39 @@ public void onViewDetachedFromWindow(View v) { mSettingsView = mDialog.findViewById(R.id.settings_container); mSettingsIcon = mDialog.findViewById(R.id.settings); + mAppVolumeView = mDialog.findViewById(R.id.app_volume_container); + mAppVolumeIcon = mDialog.findViewById(R.id.app_volume); + + mRoundedBorderBottom = mDialog.findViewById(R.id.rounded_border_bottom); + + if (mVolumePanelOnLeft) { + if (mRingerAndDrawerContainer != null) { + mRingerAndDrawerContainer.setLayoutDirection(LAYOUT_DIRECTION_RTL); + } + + ViewGroup container = mDialog.findViewById(R.id.volume_dialog_container); + setGravity(container, Gravity.LEFT); + setLayoutGravity(container, Gravity.LEFT); + + setGravity(mDialogView, Gravity.LEFT); + setLayoutGravity(mDialogView, Gravity.LEFT); + + setGravity((ViewGroup) mTopContainer, Gravity.LEFT); + + setLayoutGravity(mSelectedRingerContainer, Gravity.BOTTOM | Gravity.LEFT); + + setLayoutGravity(mRingerDrawerNewSelectionBg, Gravity.BOTTOM | Gravity.LEFT); + + setGravity(mRinger, Gravity.LEFT); + setLayoutGravity(mRinger, Gravity.BOTTOM | Gravity.LEFT); + + setGravity(mDialogRowsViewContainer, Gravity.LEFT); + setLayoutGravity(mDialogRowsViewContainer, Gravity.LEFT); + + setGravity(mODICaptionsView, Gravity.LEFT); + setLayoutGravity(mODICaptionsView, Gravity.LEFT); + } + if (mRows.isEmpty()) { if (!AudioSystem.isSingleVolume(mContext)) { addRow(STREAM_ACCESSIBILITY, R.drawable.ic_volume_accessibility, @@ -575,8 +641,13 @@ public void onViewDetachedFromWindow(View v) { addRow(AudioManager.STREAM_MUSIC, R.drawable.ic_volume_media, R.drawable.ic_volume_media_mute, true, true); if (!AudioSystem.isSingleVolume(mContext)) { - addRow(AudioManager.STREAM_RING, - R.drawable.ic_volume_ringer, R.drawable.ic_volume_ringer_mute, true, false); + if (Util.isVoiceCapable(mContext)) { + addRow(AudioManager.STREAM_RING, R.drawable.ic_volume_ringer, + R.drawable.ic_volume_ringer_mute, true, false); + } else { + addRow(AudioManager.STREAM_RING, R.drawable.ic_volume_notification, + R.drawable.ic_volume_notification_mute, true, false); + } addRow(STREAM_ALARM, R.drawable.ic_alarm, R.drawable.ic_volume_alarm_mute, true, false); addRow(AudioManager.STREAM_VOICE_CALL, @@ -594,6 +665,7 @@ public void onViewDetachedFromWindow(View v) { updateRowsH(getActiveRow()); initRingerH(); initSettingsH(lockTaskModeState); + initAppVolumeH(); initODICaptionsH(); } @@ -612,6 +684,25 @@ private void initDimens() { mRingerCount = mShowVibrate ? 3 : 2; } + // Helper to set gravity. + private void setGravity(ViewGroup viewGroup, int gravity) { + if (viewGroup instanceof LinearLayout) { + ((LinearLayout) viewGroup).setGravity(gravity); + } + } + + // Helper to set layout gravity. + private void setLayoutGravity(ViewGroup viewGroup, int gravity) { + if (viewGroup != null) { + Object obj = viewGroup.getLayoutParams(); + if (obj instanceof FrameLayout.LayoutParams) { + ((FrameLayout.LayoutParams) obj).gravity = gravity; + } else if (obj instanceof LinearLayout.LayoutParams) { + ((LinearLayout.LayoutParams) obj).gravity = gravity; + } + } + } + protected ViewGroup getDialogView() { return mDialogView; } @@ -822,6 +913,12 @@ private void setupRingerDrawer() { mDialogView.getPaddingTop(), mDialogView.getPaddingRight(), mDialogView.getPaddingBottom() + getRingerDrawerOpenExtraSize()); + } else if (mVolumePanelOnLeft) { + mDialogView.setPadding( + mDialogView.getPaddingLeft(), + mDialogView.getPaddingTop(), + mDialogView.getPaddingRight() + getRingerDrawerOpenExtraSize(), + mDialogView.getPaddingBottom()); } else { mDialogView.setPadding( mDialogView.getPaddingLeft() + getRingerDrawerOpenExtraSize(), @@ -891,15 +988,22 @@ private ImageView getDrawerIconViewForMode(int mode) { } /** - * Translation to apply form the origin (either top or left) to overlap the selection background - * with the given mode in the drawer. + * Translation to apply form the origin (either top or left/right) to overlap the selection + * background with the given mode in the drawer. */ private float getTranslationInDrawerForRingerMode(int mode) { - return mode == RINGER_MODE_VIBRATE + return (mode == RINGER_MODE_VIBRATE ? -mRingerDrawerItemSize * 2 : mode == RINGER_MODE_SILENT ? -mRingerDrawerItemSize - : 0; + : 0) + * (isLandscape() + ? getTranslationForPanelLocation() + : 1); + } + + private float getTranslationForPanelLocation() { + return mVolumePanelOnLeft ? -1 : 1; } /** Animates in the ringer drawer. */ @@ -930,12 +1034,13 @@ private void showRingerDrawer() { getTranslationInDrawerForRingerMode(mState.ringerModeInternal)); } - // Move the drawer so that the top/rightmost ringer choice overlaps with the selected ringer + // Move the drawer so that the top/outmost ringer choice overlaps with the selected ringer // icon. if (!isLandscape()) { mRingerDrawerContainer.setTranslationY(mRingerDrawerItemSize * (mRingerCount - 1)); } else { - mRingerDrawerContainer.setTranslationX(mRingerDrawerItemSize * (mRingerCount - 1)); + mRingerDrawerContainer.setTranslationX( + getTranslationForPanelLocation() * mRingerDrawerItemSize * (mRingerCount - 1)); } mRingerDrawerContainer.setAlpha(0f); mRingerDrawerContainer.setVisibility(VISIBLE); @@ -1017,7 +1122,7 @@ private void hideRingerDrawer() { .start(); } else { mRingerDrawerContainer.animate() - .translationX(mRingerDrawerItemSize * 2) + .translationX(getTranslationForPanelLocation() * mRingerDrawerItemSize * 2) .start(); } @@ -1039,6 +1144,11 @@ private void hideRingerDrawer() { } private void initSettingsH(int lockTaskModeState) { + if (mRoundedBorderBottom != null){ + mRoundedBorderBottom.setVisibility(!mDeviceProvisionedController.isCurrentUserSetup() || + lockTaskModeState != LOCK_TASK_MODE_NONE + ? VISIBLE : GONE); + } if (mSettingsView != null) { mSettingsView.setVisibility( mDeviceProvisionedController.isCurrentUserSetup() && @@ -1060,6 +1170,58 @@ private void initSettingsH(int lockTaskModeState) { } } + private boolean shouldShowAppVolume() { + mAppVolumeActivePackageName = null; + ContentResolver cr = mContext.getContentResolver(); + int showAppVolume = Settings.System.getInt(cr, Settings.System.SHOW_APP_VOLUME, 0); + boolean ret = showAppVolume == 1; + if (ret) { + ret = false; + AudioManager audioManager = mController.getAudioManager(); + for (AppVolume av : audioManager.listAppVolumes()) { + if (av.isActive()) { + mAppVolumeActivePackageName = av.getPackageName(); + return mAppVolumeActivePackageName != null; + } + } + } + return false; + } + + private Drawable getApplicationIcon(String packageName) { + PackageManager pm = mContext.getPackageManager(); + Drawable icon = null; + try { + icon = pm.getApplicationIcon(packageName); + } catch (Exception e) { + // nothing to do + } + return icon; + } + + public void initAppVolumeH() { + boolean showAppVolume = shouldShowAppVolume(); + if (mAppVolumeView != null) { + mAppVolumeView.setVisibility(showAppVolume ? VISIBLE : GONE); + } + if (mAppVolumeIcon != null && showAppVolume) { + mAppVolumeIcon.setOnClickListener(v -> { + Events.writeEvent(Events.EVENT_SETTINGS_CLICK); + Intent intent = new Intent(Settings.Panel.ACTION_APP_VOLUME); + dismissH(DISMISS_REASON_SETTINGS_CLICKED); + Dependency.get(MediaOutputDialogFactory.class).dismiss(); + Dependency.get(ActivityStarter.class).startActivity(intent, + true /* dismissShade */); + }); + Drawable icon = getApplicationIcon(mAppVolumeActivePackageName); + if (icon != null) { + mAppVolumeIcon.setImageTintList(null); + mAppVolumeIcon.setScaleType(ImageView.ScaleType.FIT_CENTER); + mAppVolumeIcon.setImageDrawable(icon); + } + } + } + public void initRingerH() { if (mRingerIcon != null) { mRingerIcon.setAccessibilityLiveRegion(ACCESSIBILITY_LIVE_REGION_POLITE); @@ -1317,7 +1479,12 @@ private void showH(int reason, boolean keyguardLocked, int lockTaskModeState) { mConfigChanged = false; } + if (mDefaultRow == null) { + mDefaultRow = getActiveRow(); + } + initSettingsH(lockTaskModeState); + initAppVolumeH(); mShowing = true; mIsAnimatingDismiss = false; mDialog.show(); @@ -1386,15 +1553,18 @@ protected void dismissH(int reason) { .withEndAction(() -> mHandler.postDelayed(() -> { mDialog.dismiss(); tryToRemoveCaptionsTooltip(); + mDefaultRow = null; mIsAnimatingDismiss = false; hideRingerDrawer(); + mController.notifyVisible(false); }, 50)); - if (!shouldSlideInVolumeTray()) animator.translationX(mDialogView.getWidth() / 2.0f); + if (!shouldSlideInVolumeTray()) { + animator.translationX(getTranslationForPanelLocation() * mDialogView.getWidth() / 2.0f); + } animator.setListener(getJankListener(getDialogView(), TYPE_DISMISS, mDialogHideAnimationDurationMs)).start(); checkODICaptionsTooltip(true); - mController.notifyVisible(false); synchronized (mSafetyWarningLock) { if (mSafetyWarning != null) { if (D.BUG) Log.d(TAG, "SafetyWarning dismissed"); @@ -1428,10 +1598,13 @@ private boolean shouldBeVisibleH(VolumeRow row, VolumeRow activeRow) { return true; } - if (row.defaultStream) { + // if the row is the default stream or the row with which this panel was created, + // show it additonally to the active row if it is one of the following streams + if (row.defaultStream || mDefaultRow == row) { return activeRow.stream == STREAM_RING || activeRow.stream == STREAM_ALARM || activeRow.stream == STREAM_VOICE_CALL + || activeRow.stream == STREAM_MUSIC || activeRow.stream == STREAM_ACCESSIBILITY || mDynamic.get(activeRow.stream); } @@ -1447,8 +1620,10 @@ private void updateRowsH(final VolumeRow activeRow) { trimObsoleteH(); } + boolean isOutmostIndexMax = mVolumePanelOnLeft ? isRtl() : !isRtl(); + // Index of the last row that is actually visible. - int rightmostVisibleRowIndex = !isRtl() ? -1 : Short.MAX_VALUE; + int outmostVisibleRowIndex = isOutmostIndexMax ? -1 : Short.MAX_VALUE; // apply changes to all rows for (final VolumeRow row : mRows) { @@ -1457,14 +1632,11 @@ private void updateRowsH(final VolumeRow activeRow) { Util.setVisOrGone(row.view, shouldBeVisible); if (shouldBeVisible && mRingerAndDrawerContainerBackground != null) { - // For RTL, the rightmost row has the lowest index since child views are laid out + // For RTL, the outmost row has the lowest index since child views are laid out // from right to left. - rightmostVisibleRowIndex = - !isRtl() - ? Math.max(rightmostVisibleRowIndex, - mDialogRowsView.indexOfChild(row.view)) - : Math.min(rightmostVisibleRowIndex, - mDialogRowsView.indexOfChild(row.view)); + outmostVisibleRowIndex = isOutmostIndexMax + ? Math.max(outmostVisibleRowIndex, mDialogRowsView.indexOfChild(row.view)) + : Math.min(outmostVisibleRowIndex, mDialogRowsView.indexOfChild(row.view)); // Add spacing between each of the visible rows - we'll remove the spacing from the // last row after the loop. @@ -1472,7 +1644,7 @@ private void updateRowsH(final VolumeRow activeRow) { if (layoutParams instanceof LinearLayout.LayoutParams) { final LinearLayout.LayoutParams linearLayoutParams = ((LinearLayout.LayoutParams) layoutParams); - if (!isRtl()) { + if (isOutmostIndexMax) { linearLayoutParams.setMarginEnd(mRingerRowsPadding); } else { linearLayoutParams.setMarginStart(mRingerRowsPadding); @@ -1490,8 +1662,8 @@ private void updateRowsH(final VolumeRow activeRow) { } } - if (rightmostVisibleRowIndex > -1 && rightmostVisibleRowIndex < Short.MAX_VALUE) { - final View lastVisibleChild = mDialogRowsView.getChildAt(rightmostVisibleRowIndex); + if (outmostVisibleRowIndex > -1 && outmostVisibleRowIndex < Short.MAX_VALUE) { + final View lastVisibleChild = mDialogRowsView.getChildAt(outmostVisibleRowIndex); final ViewGroup.LayoutParams layoutParams = lastVisibleChild.getLayoutParams(); // Remove the spacing on the last row, and remove its background since the container is // drawing a background for this row. @@ -1615,16 +1787,20 @@ private void trimObsoleteH() { final VolumeRow row = mRows.get(i); if (row.ss == null || !row.ss.dynamic) continue; if (!mDynamic.get(row.stream)) { - mRows.remove(i); - mDialogRowsView.removeView(row.view); + removeRow(row); mConfigurableTexts.remove(row.header); } } } + private void removeRow(VolumeRow volumeRow) { + mRows.remove(volumeRow); + mDialogRowsView.removeView(volumeRow.view); + } + protected void onStateChangedH(State state) { if (D.BUG) Log.d(TAG, "onStateChangedH() state: " + state.toString()); - if (mState != null && state != null + if (mShowing && mState != null && state != null && mState.ringerModeInternal != -1 && mState.ringerModeInternal != state.ringerModeInternal && state.ringerModeInternal == AudioManager.RINGER_MODE_VIBRATE) { @@ -1645,6 +1821,10 @@ protected void onStateChangedH(State state) { } } + if (Util.isVoiceCapable(mContext)) { + updateNotificationRowH(); + } + if (mActiveStream != state.activeStream) { mPrevActiveStream = mActiveStream; mActiveStream = state.activeStream; @@ -1663,6 +1843,16 @@ CharSequence composeWindowTitle() { return mContext.getString(R.string.volume_dialog_title, getStreamLabelH(getActiveRow().ss)); } + private void updateNotificationRowH() { + VolumeRow notificationRow = findRow(AudioManager.STREAM_NOTIFICATION); + if (notificationRow != null && mState.linkedNotification) { + removeRow(notificationRow); + } else if (notificationRow == null && !mState.linkedNotification) { + addRow(AudioManager.STREAM_NOTIFICATION, R.drawable.ic_volume_notification, + R.drawable.ic_volume_notification_mute, true, false); + } + } + private void updateVolumeRowH(VolumeRow row) { if (D.BUG) Log.i(TAG, "updateVolumeRowH s=" + row.stream); if (mState == null) return; @@ -1680,19 +1870,22 @@ private void updateVolumeRowH(VolumeRow row) { final boolean isSystemStream = row.stream == AudioManager.STREAM_SYSTEM; final boolean isAlarmStream = row.stream == STREAM_ALARM; final boolean isMusicStream = row.stream == AudioManager.STREAM_MUSIC; - final boolean isRingVibrate = isRingStream - && mState.ringerModeInternal == AudioManager.RINGER_MODE_VIBRATE; + final boolean isNotificationStream = row.stream == AudioManager.STREAM_NOTIFICATION; + final boolean isVibrate = mState.ringerModeInternal == AudioManager.RINGER_MODE_VIBRATE; + final boolean isRingVibrate = isRingStream && isVibrate; final boolean isRingSilent = isRingStream && mState.ringerModeInternal == AudioManager.RINGER_MODE_SILENT; final boolean isZenPriorityOnly = mState.zenMode == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS; final boolean isZenAlarms = mState.zenMode == Global.ZEN_MODE_ALARMS; final boolean isZenNone = mState.zenMode == Global.ZEN_MODE_NO_INTERRUPTIONS; - final boolean zenMuted = isZenAlarms ? (isRingStream || isSystemStream) - : isZenNone ? (isRingStream || isSystemStream || isAlarmStream || isMusicStream) + final boolean zenMuted = + isZenAlarms ? (isRingStream || isSystemStream || isNotificationStream) + : isZenNone ? (isRingStream || isSystemStream || isAlarmStream || isMusicStream || isNotificationStream) : isZenPriorityOnly ? ((isAlarmStream && mState.disallowAlarms) || (isMusicStream && mState.disallowMedia) || (isRingStream && mState.disallowRinger) || (isSystemStream && mState.disallowSystem)) + : isVibrate ? isNotificationStream : false; // update slider max @@ -1977,6 +2170,21 @@ private int getRingerDrawerOpenExtraSize() { return (mRingerCount - 1) * mRingerDrawerItemSize; } + /** + * Return the size of the additionally visible rows next to the default stream. + * An additional row is visible for example while receiving a voice call. + */ + private int getVisibleRowsExtraSize() { + VolumeRow activeRow = getActiveRow(); + int visibleRows = 0; + for (final VolumeRow row : mRows) { + if (shouldBeVisibleH(row, activeRow)) { + visibleRows++; + } + } + return (visibleRows - 1) * (mDialogWidth + mRingerRowsPadding); + } + private void updateBackgroundForDrawerClosedAmount() { if (mRingerAndDrawerContainerBackground == null) { return; @@ -1985,6 +2193,9 @@ private void updateBackgroundForDrawerClosedAmount() { final Rect bounds = mRingerAndDrawerContainerBackground.copyBounds(); if (!isLandscape()) { bounds.top = (int) (mRingerDrawerClosedAmount * getRingerDrawerOpenExtraSize()); + } else if (mVolumePanelOnLeft) { + bounds.right = (int) ((mDialogCornerRadius / 2) + mRingerDrawerItemSize + + (1f - mRingerDrawerClosedAmount) * getRingerDrawerOpenExtraSize()); } else { bounds.left = (int) (mRingerDrawerClosedAmount * getRingerDrawerOpenExtraSize()); } @@ -1992,7 +2203,7 @@ private void updateBackgroundForDrawerClosedAmount() { } /* - * The top container is responsible for drawing the solid color background behind the rightmost + * The top container is responsible for drawing the solid color background behind the outmost * (primary) volume row. This is because the volume drawer animates in from below, initially * overlapping the primary row. We need the drawer to draw below the row's SeekBar, since it * looks strange to overlap it, but above the row's background color, since otherwise it will be @@ -2026,8 +2237,9 @@ private void setTopContainerBackgroundDrawable() { ? mDialogRowsViewContainer.getTop() : mDialogRowsViewContainer.getTop() - mDialogCornerRadius); - // Set gravity to top-right, since additional rows will be added on the left. - background.setLayerGravity(0, Gravity.TOP | Gravity.RIGHT); + // Set gravity to top and opposite side where additional rows will be added. + background.setLayerGravity( + 0, mVolumePanelOnLeft ? Gravity.TOP | Gravity.LEFT : Gravity.TOP | Gravity.RIGHT); // In landscape, the ringer drawer animates out to the left (instead of down). Since the // drawer comes from the right (beyond the bounds of the dialog), we should clip it so it diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java index d03148cee335..83def7645c13 100644 --- a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java +++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java @@ -209,14 +209,14 @@ protected void onResume() { true, Utils.getColorAttrDefaultColor( this, com.android.internal.R.attr.colorAccentPrimary)); - mKeyguardViewManager.requestFace(true); + //mKeyguardViewManager.requestFace(true); } @Override protected void onPause() { super.onPause(); mKeyguardViewManager.requestFp(false, -1); - mKeyguardViewManager.requestFace(false); + //mKeyguardViewManager.requestFace(false); } @Override diff --git a/packages/SystemUI/src/com/google/hardware/pixel/display/HbmState.aidl b/packages/SystemUI/src/com/google/hardware/pixel/display/HbmState.aidl new file mode 100644 index 000000000000..39270a2878c1 --- /dev/null +++ b/packages/SystemUI/src/com/google/hardware/pixel/display/HbmState.aidl @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.hardware.pixel.display; + +@VintfStability +@Backing(type="byte") +enum HbmState { + OFF = 0, + HDR = 1, + SUNLIGHT = 2, +} diff --git a/packages/SystemUI/src/com/google/hardware/pixel/display/IDisplay.aidl b/packages/SystemUI/src/com/google/hardware/pixel/display/IDisplay.aidl new file mode 100644 index 000000000000..5787cd5c26ca --- /dev/null +++ b/packages/SystemUI/src/com/google/hardware/pixel/display/IDisplay.aidl @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.hardware.pixel.display; + +import com.google.hardware.pixel.display.HbmState; +import com.google.hardware.pixel.display.LbeState; + +interface IDisplay { + /** + * Query Display High Brightness Mode Supported + * + * @return true if HBM is supported on this platform. + * If false, HBM is not supported. + */ + boolean isHbmSupported(); + + /** + * Set Display High Brightness Mode + * + * @param state to be queried. + */ + void setHbmState(in HbmState state); + + /** + * Get Display High Brightness Mode State + * + * @return state of display high brightness mode + */ + HbmState getHbmState(); + + /** + * Query Display Local Brightness Enhancement Supported + * + * @return true if LBE is supported on this platform. + * If false, LBE is not supported. + */ + boolean isLbeSupported(); + + /** + * Set Display Local Brightness Enhancement + * + * @param state to be queried. + */ + void setLbeState(in LbeState state); + + /** + * Set Display Local Brightness Enhancement Ambient Light + * + * @param ambientLux the proper ambient light in Lux. + * The value is equal to zero or above zero. + */ + void setLbeAmbientLight(in int ambientLux); + + /** + * Get Display Local Brightness Enhancement State + * + * @return state of display local brightness enhancement + */ + LbeState getLbeState(); + + /** + * Query Display Local High Brightness Mode Supported + * + * @return true if LHBM is supported on this platform. + * If false, LHBM is not supported. + */ + boolean isLhbmSupported(); + + /** + * Set Display Local High Brightness Mode + * + * @param enabled true if LHBM should be enabled, false otherwise. + */ + void setLhbmState(in boolean enabled); + + /** + * Get Display Local High Brightness Mode State + * + * @return true if LHBM is ON, false otherwise. + */ + boolean getLhbmState(); +} diff --git a/packages/SystemUI/src/com/google/hardware/pixel/display/LbeState.aidl b/packages/SystemUI/src/com/google/hardware/pixel/display/LbeState.aidl new file mode 100644 index 000000000000..810dcc7c5f14 --- /dev/null +++ b/packages/SystemUI/src/com/google/hardware/pixel/display/LbeState.aidl @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.hardware.pixel.display; + +@VintfStability +@Backing(type="byte") +enum LbeState { + OFF = 0, + NORMAL = 1, + HIGH_BRIGHTNESS = 2, + POWER_SAVE = 3, +} diff --git a/packages/SystemUI/tests/Android.mk b/packages/SystemUI/tests/Android.mk index ff5165d4e7cf..8e6b65d0365f 100644 --- a/packages/SystemUI/tests/Android.mk +++ b/packages/SystemUI/tests/Android.mk @@ -42,6 +42,7 @@ LOCAL_JAVA_LIBRARIES := \ android.test.runner \ telephony-common \ android.test.base \ + ims-common LOCAL_AAPT_FLAGS := --extra-packages com.android.systemui diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockAccessibilityDelegateTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockAccessibilityDelegateTest.java deleted file mode 100644 index 00f88bfa2abb..000000000000 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockAccessibilityDelegateTest.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright (C) 2017 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.keyguard; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import android.text.TextUtils; -import android.view.accessibility.AccessibilityEvent; -import android.view.accessibility.AccessibilityNodeInfo; -import android.widget.TextView; - -import androidx.test.filters.SmallTest; - -import com.android.systemui.R; -import com.android.systemui.SysuiTestCase; - -import org.junit.Before; -import org.junit.Test; - -import java.util.List; - -@SmallTest -public class KeyguardClockAccessibilityDelegateTest extends SysuiTestCase { - - private TextView mView; - private String m12HoursFormat; - private String m24HoursFormat; - - @Before - public void setUp() throws Exception { - m12HoursFormat = mContext.getString(R.string.keyguard_widget_12_hours_format); - m24HoursFormat = mContext.getString(R.string.keyguard_widget_24_hours_format); - - mView = new TextView(mContext); - mView.setText(m12HoursFormat); - mView.setContentDescription(m12HoursFormat); - mView.setAccessibilityDelegate(new KeyguardClockAccessibilityDelegate(mContext)); - } - - @Test - public void onInitializeAccessibilityEvent_producesNonEmptyAsciiContentDesc() throws Exception { - AccessibilityEvent ev = AccessibilityEvent.obtain(); - mView.onInitializeAccessibilityEvent(ev); - - assertFalse(TextUtils.isEmpty(ev.getContentDescription())); - assertTrue(isAscii(ev.getContentDescription())); - } - - @Test - public void onPopulateAccessibilityEvent_producesNonEmptyAsciiText() throws Exception { - AccessibilityEvent ev = AccessibilityEvent.obtain(); - mView.onPopulateAccessibilityEvent(ev); - - assertFalse(isEmpty(ev.getText())); - assertTrue(isAscii(ev.getText())); - } - - @Test - public void onInitializeAccessibilityNodeInfo_producesNonEmptyAsciiText() throws Exception { - AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain(); - // Usually done in View.onInitializeAccessibilityNodeInfoInternal, but only when attached. - info.setContentDescription(mView.getContentDescription()); - mView.onInitializeAccessibilityNodeInfo(info); - - assertFalse(TextUtils.isEmpty(info.getText())); - assertTrue(isAscii(info.getText())); - - assertFalse(TextUtils.isEmpty(info.getContentDescription())); - assertTrue(isAscii(info.getContentDescription())); - } - - @Test - public void isNeeded_returnsTrueIfDateFormatsContainNonAscii() { - if (!isAscii(m12HoursFormat) || !isAscii(m24HoursFormat)) { - assertTrue(KeyguardClockAccessibilityDelegate.isNeeded(mContext)); - } - } - - @Test - public void isNeeded_returnsWhetherFancyColonExists() { - boolean hasFancyColon = !TextUtils.isEmpty(mContext.getString( - R.string.keyguard_fancy_colon)); - - assertEquals(hasFancyColon, KeyguardClockAccessibilityDelegate.isNeeded(mContext)); - } - - private boolean isAscii(CharSequence text) { - return text.chars().allMatch((i) -> i < 128); - } - - private boolean isAscii(List texts) { - return texts.stream().allMatch(this::isAscii); - } - - private boolean isEmpty(List texts) { - return texts.stream().allMatch(TextUtils::isEmpty); - } -} \ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/HeaderPrivacyIconsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/HeaderPrivacyIconsControllerTest.kt index 4c7240673fb0..3620233fc9df 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/HeaderPrivacyIconsControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/HeaderPrivacyIconsControllerTest.kt @@ -19,6 +19,7 @@ import com.android.systemui.privacy.PrivacyDialogController import com.android.systemui.privacy.PrivacyItemController import com.android.systemui.privacy.logging.PrivacyLogger import com.android.systemui.statusbar.phone.StatusIconContainer +import com.android.systemui.statusbar.policy.DeviceProvisionedController import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.argumentCaptor @@ -66,6 +67,8 @@ class HeaderPrivacyIconsControllerTest : SysuiTestCase() { private lateinit var broadcastDispatcher: BroadcastDispatcher @Mock private lateinit var safetyCenterManager: SafetyCenterManager + @Mock + private lateinit var deviceProvisionedController: DeviceProvisionedController private val uiExecutor = FakeExecutor(FakeSystemClock()) private val backgroundExecutor = FakeExecutor(FakeSystemClock()) @@ -80,6 +83,7 @@ class HeaderPrivacyIconsControllerTest : SysuiTestCase() { whenever(privacyChip.context).thenReturn(context) whenever(privacyChip.resources).thenReturn(context.resources) whenever(privacyChip.isAttachedToWindow).thenReturn(true) + whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true) cameraSlotName = context.getString(com.android.internal.R.string.status_bar_camera) microphoneSlotName = context.getString(com.android.internal.R.string.status_bar_microphone) @@ -98,7 +102,8 @@ class HeaderPrivacyIconsControllerTest : SysuiTestCase() { activityStarter, appOpsController, broadcastDispatcher, - safetyCenterManager + safetyCenterManager, + deviceProvisionedController ) backgroundExecutor.runAllReady() @@ -199,6 +204,18 @@ class HeaderPrivacyIconsControllerTest : SysuiTestCase() { ) } + @Test + fun testNoDialogWhenDeviceNotProvisioned() { + whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(false) + controller.onParentVisible() + + val captor = argumentCaptor() + verify(privacyChip).setOnClickListener(capture(captor)) + + captor.value.onClick(privacyChip) + verify(privacyDialogController, never()).showDialog(any(Context::class.java)) + } + private fun setPrivacyController(micCamera: Boolean, location: Boolean) { whenever(privacyItemController.micCameraAvailable).thenReturn(micCamera) whenever(privacyItemController.locationAvailable).thenReturn(location) diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java index 3cad2a005882..b847ad07cd72 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java @@ -277,7 +277,7 @@ public void testChangeConfiguration_shouldUseHorizontalLayout() { // Then the layout changes assertThat(mController.shouldUseHorizontalLayout()).isTrue(); - verify(mHorizontalLayoutListener).run(); // not invoked + verify(mHorizontalLayoutListener).run(); // When it is rotated back to portrait mConfiguration.orientation = Configuration.ORIENTATION_PORTRAIT; @@ -300,4 +300,24 @@ public void testRefreshAllTilesDoesntRefreshListeningTiles() { verify(mQSTile).refreshState(); verify(mOtherTile, never()).refreshState(); } + + @Test + public void configurationChange_onlySplitShadeConfigChanges_horizontalLayoutStatusUpdated() { + // Preconditions for horizontal layout + when(mMediaHost.getVisible()).thenReturn(true); + when(mResources.getBoolean(R.bool.config_use_split_notification_shade)).thenReturn(false); + mConfiguration.orientation = Configuration.ORIENTATION_LANDSCAPE; + mController.setUsingHorizontalLayoutChangeListener(mHorizontalLayoutListener); + mController.mOnConfigurationChangedListener.onConfigurationChange(mConfiguration); + assertThat(mController.shouldUseHorizontalLayout()).isTrue(); + reset(mHorizontalLayoutListener); + + // Only split shade status changes + when(mResources.getBoolean(R.bool.config_use_split_notification_shade)).thenReturn(true); + mController.mOnConfigurationChangedListener.onConfigurationChange(mConfiguration); + + // Horizontal layout is updated accordingly. + assertThat(mController.shouldUseHorizontalLayout()).isFalse(); + verify(mHorizontalLayoutListener).run(); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt index 5eb9a9862340..e539705d9ede 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt @@ -6,6 +6,7 @@ import com.android.internal.logging.MetricsLogger import com.android.internal.logging.UiEventLogger import com.android.systemui.SysuiTestCase import com.android.systemui.dump.DumpManager +import com.android.systemui.flags.FeatureFlags import com.android.systemui.media.MediaHost import com.android.systemui.media.MediaHostState import com.android.systemui.plugins.FalsingManager @@ -52,6 +53,7 @@ class QSPanelControllerTest : SysuiTestCase() { @Mock private lateinit var tile: QSTile @Mock private lateinit var otherTile: QSTile @Mock private lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager + @Mock private lateinit var featureFlags: FeatureFlags private lateinit var controller: QSPanelController @@ -82,7 +84,8 @@ class QSPanelControllerTest : SysuiTestCase() { brightnessControllerFactory, brightnessSliderFactory, falsingManager, - statusBarKeyguardViewManager + statusBarKeyguardViewManager, + featureFlags ) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt index 2db58be15665..7c930b196d68 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt @@ -158,6 +158,32 @@ class QSPanelTest : SysuiTestCase() { assertThat(qsPanel.paddingBottom).isEqualTo(padding) } + @Test + fun testTopPadding_notCombinedHeaders() { + qsPanel.setUsingCombinedHeaders(false) + val padding = 10 + val paddingCombined = 100 + context.orCreateTestableResources.addOverride(R.dimen.qs_panel_padding_top, padding) + context.orCreateTestableResources.addOverride( + R.dimen.qs_panel_padding_top_combined_headers, paddingCombined) + + qsPanel.updatePadding() + assertThat(qsPanel.paddingTop).isEqualTo(padding) + } + + @Test + fun testTopPadding_combinedHeaders() { + qsPanel.setUsingCombinedHeaders(true) + val padding = 10 + val paddingCombined = 100 + context.orCreateTestableResources.addOverride(R.dimen.qs_panel_padding_top, padding) + context.orCreateTestableResources.addOverride( + R.dimen.qs_panel_padding_top_combined_headers, paddingCombined) + + qsPanel.updatePadding() + assertThat(qsPanel.paddingTop).isEqualTo(paddingCombined) + } + @Test fun testSetSquishinessFraction_noCrash() { qsPanel.addView(qsPanel.mTileLayout as View, 0) diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt index 6af8e4904a1e..a737432a4a49 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt @@ -58,6 +58,7 @@ class QuickQSPanelControllerTest : SysuiTestCase() { @Mock private lateinit var tile: QSTile @Mock private lateinit var tileLayout: TileLayout @Mock private lateinit var tileView: QSTileView + @Mock private lateinit var quickQsBrightnessController: QuickQSBrightnessController @Captor private lateinit var captor: ArgumentCaptor private val uiEventLogger = UiEventLoggerFake() @@ -88,7 +89,8 @@ class QuickQSPanelControllerTest : SysuiTestCase() { metricsLogger, uiEventLogger, qsLogger, - dumpManager) + dumpManager, + quickQsBrightnessController) controller.init() } @@ -145,6 +147,10 @@ class QuickQSPanelControllerTest : SysuiTestCase() { verify(mediaHost).expansion = MediaHostState.EXPANDED } + fun testBrightnessVisibilityRefreshedWhenConfigurationChanged() { + verify(quickQsBrightnessController).refreshVisibility(anyBoolean()) + } + class TestQuickQSPanelController( view: QuickQSPanel, qsTileHost: QSTileHost, diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt index 39d89bf99af2..3847901e7921 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt @@ -23,7 +23,6 @@ import androidx.test.filters.SmallTest import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.android.systemui.battery.BatteryMeterViewController -import com.android.systemui.colorextraction.SysuiColorExtractor import com.android.systemui.demomode.DemoModeController import com.android.systemui.flags.FeatureFlags import com.android.systemui.qs.carrier.QSCarrierGroup @@ -70,8 +69,6 @@ class QuickStatusBarHeaderControllerTest : SysuiTestCase() { @Mock private lateinit var qsCarrierGroupController: QSCarrierGroupController @Mock - private lateinit var colorExtractor: SysuiColorExtractor - @Mock private lateinit var iconContainer: StatusIconContainer @Mock private lateinit var qsCarrierGroup: QSCarrierGroup @@ -122,7 +119,6 @@ class QuickStatusBarHeaderControllerTest : SysuiTestCase() { demoModeController, quickQSPanelController, qsCarrierGroupControllerBuilder, - colorExtractor, qsExpansionPathInterpolator, featureFlags, variableDateViewControllerFactory, diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ImageExporterTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ImageExporterTest.java index 7d563399ee1c..7dee1a3bfd88 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ImageExporterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ImageExporterTest.java @@ -65,12 +65,12 @@ public class ImageExporterTest extends SysuiTestCase { private static final byte[] EXIF_FILE_TAG = "Exif\u0000\u0000".getBytes(US_ASCII); private static final ZonedDateTime CAPTURE_TIME = - ZonedDateTime.of(LocalDateTime.of(2020, 12, 15, 13, 15), ZoneId.of("EST")); + ZonedDateTime.of(LocalDateTime.of(2020, 12, 15, 13, 15), ZoneId.of("America/New_York")); @Test public void testImageFilename() { assertEquals("image file name", "Screenshot_20201215-131500.png", - ImageExporter.createFilename(CAPTURE_TIME, CompressFormat.PNG)); + ImageExporter.createFilename(CAPTURE_TIME, CompressFormat.PNG, null)); } @Test @@ -97,7 +97,7 @@ public void testImageExport() throws ExecutionException, InterruptedException, I Bitmap original = createCheckerBitmap(10, 10, 10); ListenableFuture direct = - exporter.export(DIRECT_EXECUTOR, requestId, original, CAPTURE_TIME); + exporter.export(DIRECT_EXECUTOR, requestId, original, CAPTURE_TIME, null); assertTrue("future should be done", direct.isDone()); assertFalse("future should not be canceled", direct.isCancelled()); ImageExporter.Result result = direct.get(); @@ -150,7 +150,7 @@ public void testImageExport() throws ExecutionException, InterruptedException, I @Test public void testMediaStoreMetadata() { - String name = ImageExporter.createFilename(CAPTURE_TIME, CompressFormat.PNG); + String name = ImageExporter.createFilename(CAPTURE_TIME, CompressFormat.PNG, null); ContentValues values = ImageExporter.createMetadata(CAPTURE_TIME, CompressFormat.PNG, name); assertEquals("Pictures/Screenshots", values.getAsString(MediaStore.MediaColumns.RELATIVE_PATH)); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java index dcce61b86ced..5a12fcb1b4d8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java @@ -16,6 +16,8 @@ package com.android.systemui.statusbar.phone; +import static com.android.systemui.flags.Flags.MODERN_BOUNCER; + import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; @@ -525,4 +527,11 @@ public void testReportBouncerOnDreamWhenNotVisible() { mBouncerExpansionCallback.onVisibilityChanged(false); verify(mCentralSurfaces).setBouncerShowingOverDream(false); } + + @Test + public void flag_off_DoesNotCallBouncerInteractor() { + when(mFeatureFlags.isEnabled(MODERN_BOUNCER)).thenReturn(false); + mStatusBarKeyguardViewManager.hideBouncer(false); + verify(mBouncerInteractor, never()).hide(); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java index 2e74bf5474f9..2f9044aa4c9c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java @@ -49,6 +49,7 @@ import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; +import com.android.systemui.tuner.TunerService; import org.junit.Before; import org.junit.Test; @@ -89,6 +90,7 @@ public class VolumeDialogImplTest extends SysuiTestCase { @Mock ActivityStarter mActivityStarter; @Mock + TunerService mTunerService; InteractionJankMonitor mInteractionJankMonitor; @Before @@ -106,6 +108,7 @@ public void setup() throws Exception { mMediaOutputDialogFactory, mVolumePanelFactory, mActivityStarter, + mTunerService, mInteractionJankMonitor); mDialog.init(0, null); State state = createShellState(); diff --git a/packages/overlays/Android.mk b/packages/overlays/Android.mk index 69641e69a9f2..5934b6a6264d 100644 --- a/packages/overlays/Android.mk +++ b/packages/overlays/Android.mk @@ -26,7 +26,9 @@ LOCAL_REQUIRED_MODULES := \ DisplayCutoutEmulationTallOverlay \ DisplayCutoutEmulationWaterfallOverlay \ FontNotoSerifSourceOverlay \ + NavigationBarMode2ButtonOverlay \ NavigationBarMode3ButtonOverlay \ + NavigationBarMode2ButtonOverlay \ NavigationBarModeGesturalOverlay \ NavigationBarModeGesturalOverlayNarrowBack \ NavigationBarModeGesturalOverlayWideBack \ diff --git a/packages/overlays/IconPackCircularSystemUIOverlay/res/drawable/ic_volume_notification.xml b/packages/overlays/IconPackCircularSystemUIOverlay/res/drawable/ic_volume_notification.xml new file mode 100644 index 000000000000..6e67ab7a0f3a --- /dev/null +++ b/packages/overlays/IconPackCircularSystemUIOverlay/res/drawable/ic_volume_notification.xml @@ -0,0 +1,31 @@ + + + + + + + diff --git a/packages/overlays/IconPackCircularSystemUIOverlay/res/drawable/ic_volume_notification_mute.xml b/packages/overlays/IconPackCircularSystemUIOverlay/res/drawable/ic_volume_notification_mute.xml new file mode 100644 index 000000000000..9c7ea09d1cfc --- /dev/null +++ b/packages/overlays/IconPackCircularSystemUIOverlay/res/drawable/ic_volume_notification_mute.xml @@ -0,0 +1,33 @@ + + + + + + + + diff --git a/packages/overlays/IconPackFilledSystemUIOverlay/res/drawable/ic_volume_notification.xml b/packages/overlays/IconPackFilledSystemUIOverlay/res/drawable/ic_volume_notification.xml new file mode 100644 index 000000000000..199e8e868c69 --- /dev/null +++ b/packages/overlays/IconPackFilledSystemUIOverlay/res/drawable/ic_volume_notification.xml @@ -0,0 +1,30 @@ + + + + + + + diff --git a/packages/overlays/IconPackFilledSystemUIOverlay/res/drawable/ic_volume_notification_mute.xml b/packages/overlays/IconPackFilledSystemUIOverlay/res/drawable/ic_volume_notification_mute.xml new file mode 100644 index 000000000000..574eb40fc05d --- /dev/null +++ b/packages/overlays/IconPackFilledSystemUIOverlay/res/drawable/ic_volume_notification_mute.xml @@ -0,0 +1,33 @@ + + + + + + + + diff --git a/packages/overlays/IconPackRoundedSystemUIOverlay/res/drawable/ic_volume_notification.xml b/packages/overlays/IconPackRoundedSystemUIOverlay/res/drawable/ic_volume_notification.xml new file mode 100644 index 000000000000..0471e6a59d1d --- /dev/null +++ b/packages/overlays/IconPackRoundedSystemUIOverlay/res/drawable/ic_volume_notification.xml @@ -0,0 +1,33 @@ + + + + + + + + diff --git a/packages/overlays/IconPackRoundedSystemUIOverlay/res/drawable/ic_volume_notification_mute.xml b/packages/overlays/IconPackRoundedSystemUIOverlay/res/drawable/ic_volume_notification_mute.xml new file mode 100644 index 000000000000..0471e6a59d1d --- /dev/null +++ b/packages/overlays/IconPackRoundedSystemUIOverlay/res/drawable/ic_volume_notification_mute.xml @@ -0,0 +1,33 @@ + + + + + + + + diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto index 3801c2473c11..ee0822325d1e 100644 --- a/proto/src/metrics_constants/metrics_constants.proto +++ b/proto/src/metrics_constants/metrics_constants.proto @@ -7437,5 +7437,8 @@ message MetricsEvent { // ---- End Q Constants, all Q constants go above this line ---- // Add new aosp constants above this line. // END OF AOSP CONSTANTS + + // Bootleggers Metrics + BOOTLEG = 3210; } } diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 6417db0eb050..29194c58bd0c 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -657,25 +657,27 @@ public void onPackageRemoved(String packageName, int uid) { userState.mBindingServices.removeIf(filter); userState.mCrashedServices.removeIf(filter); final Iterator it = userState.mEnabledServices.iterator(); + boolean anyServiceRemoved = false; while (it.hasNext()) { final ComponentName comp = it.next(); final String compPkg = comp.getPackageName(); if (compPkg.equals(packageName)) { it.remove(); - // Update the enabled services setting. - persistComponentNamesToSettingLocked( - Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, - userState.mEnabledServices, userId); - // Update the touch exploration granted services setting. userState.mTouchExplorationGrantedServices.remove(comp); - persistComponentNamesToSettingLocked( - Settings.Secure. - TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES, - userState.mTouchExplorationGrantedServices, userId); - onUserStateChangedLocked(userState); - return; + anyServiceRemoved = true; } } + if (anyServiceRemoved) { + // Update the enabled services setting. + persistComponentNamesToSettingLocked( + Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, + userState.mEnabledServices, userId); + // Update the touch exploration granted services setting. + persistComponentNamesToSettingLocked( + Settings.Secure.TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES, + userState.mTouchExplorationGrantedServices, userId); + onUserStateChangedLocked(userState); + } } } diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetXmlUtil.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetXmlUtil.java index 6a5dcc8c5945..5c0f0a5c713a 100644 --- a/services/appwidget/java/com/android/server/appwidget/AppWidgetXmlUtil.java +++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetXmlUtil.java @@ -93,7 +93,7 @@ public static void writeAppWidgetProviderInfoLocked(@NonNull final TypedXmlSeria out.attributeInt(null, ATTR_WIDGET_CATEGORY, info.widgetCategory); out.attributeInt(null, ATTR_WIDGET_FEATURES, info.widgetFeatures); out.attributeInt(null, ATTR_DESCRIPTION_RES, info.descriptionRes); - out.attribute(null, ATTR_OS_FINGERPRINT, Build.FINGERPRINT); + out.attribute(null, ATTR_OS_FINGERPRINT, String.valueOf(Build.TIME)); } /** @@ -104,7 +104,7 @@ public static AppWidgetProviderInfo readAppWidgetProviderInfoLocked( @NonNull final TypedXmlPullParser parser) { Objects.requireNonNull(parser); final String fingerprint = parser.getAttributeValue(null, ATTR_OS_FINGERPRINT); - if (!Build.FINGERPRINT.equals(fingerprint)) { + if (!String.valueOf(Build.TIME).equals(fingerprint)) { return null; } final AppWidgetProviderInfo info = new AppWidgetProviderInfo(); diff --git a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java index 8525e3634e3a..592045c23372 100644 --- a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java +++ b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java @@ -245,6 +245,7 @@ void dispatchCancellation(@Nullable ICancellationSignal cancellation) { }); } + @SuppressWarnings("ReturnValueIgnored") private void maybeRequestShowInlineSuggestions(int sessionId, @Nullable InlineSuggestionsRequest request, @Nullable List inlineSuggestionsData, @Nullable Bundle clientState, diff --git a/services/backup/java/com/android/server/backup/OperationStorage.java b/services/backup/java/com/android/server/backup/OperationStorage.java index 466f647fd034..8f73436d193a 100644 --- a/services/backup/java/com/android/server/backup/OperationStorage.java +++ b/services/backup/java/com/android/server/backup/OperationStorage.java @@ -153,4 +153,4 @@ void registerOperationForPackages(int token, @OpState int initialState, * @return a set of operation tokens for operations in that state. */ Set operationTokensForOpState(@OpState int state); -}; +} diff --git a/services/backup/java/com/android/server/backup/internal/LifecycleOperationStorage.java b/services/backup/java/com/android/server/backup/internal/LifecycleOperationStorage.java index 6908c60034b6..a94167eb9fa7 100644 --- a/services/backup/java/com/android/server/backup/internal/LifecycleOperationStorage.java +++ b/services/backup/java/com/android/server/backup/internal/LifecycleOperationStorage.java @@ -353,4 +353,4 @@ public void cancelOperation( op.callback.handleCancel(cancelAll); } } -}; +} diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java index ff1a495edcbb..36b82f845ce3 100644 --- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java +++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java @@ -657,7 +657,6 @@ public void startSession(@NonNull IBinder activityToken, int sessionId, int flags, @NonNull IResultReceiver result) { Objects.requireNonNull(activityToken); Objects.requireNonNull(shareableActivityToken); - Objects.requireNonNull(sessionId); final int userId = UserHandle.getCallingUserId(); final ActivityPresentationInfo activityPresentationInfo = getAmInternal() @@ -676,7 +675,6 @@ public void startSession(@NonNull IBinder activityToken, @Override public void finishSession(int sessionId) { - Objects.requireNonNull(sessionId); final int userId = UserHandle.getCallingUserId(); synchronized (mLock) { diff --git a/services/core/Android.bp b/services/core/Android.bp index 89c8ca567dd9..2091c0c4e6d9 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -19,7 +19,10 @@ filegroup { filegroup { name: "services.core-sources", - srcs: ["java/**/*.java"], + srcs: [ + "java/**/*.java", + "java/**/*.kt", + ], exclude_srcs: [ ":services.core-sources-am-wm", ], @@ -173,6 +176,10 @@ java_library_static { "overlayable_policy_aidl-java", "SurfaceFlingerProperties", "com.android.sysprop.watchdog", + "faceunlock_framework", + "kotlin-stdlib", + "kotlinx_coroutines_android", + "kotlinx_coroutines", ], javac_shard_size: 50, } diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java index 2f8dea7b5f14..0c0639f563d7 100644 --- a/services/core/java/com/android/server/BatteryService.java +++ b/services/core/java/com/android/server/BatteryService.java @@ -67,9 +67,12 @@ import com.android.server.lights.LightsManager; import com.android.server.lights.LogicalLight; +import java.io.BufferedReader; import java.io.File; import java.io.FileDescriptor; +import java.io.FileNotFoundException; import java.io.FileOutputStream; +import java.io.FileReader; import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayDeque; @@ -165,6 +168,18 @@ public final class BatteryService extends SystemService { private boolean mBatteryLevelLow; + private boolean mDashCharger; + private boolean mHasDashCharger; + private boolean mLastDashCharger; + + private boolean mWarpCharger; + private boolean mHasWarpCharger; + private boolean mLastWarpCharger; + + private boolean mVoocCharger; + private boolean mHasVoocCharger; + private boolean mLastVoocCharger; + private long mDischargeStartTime; private int mDischargeStartLevel; @@ -196,6 +211,16 @@ public BatteryService(Context context) { mBatteryStats = BatteryStatsService.getService(); mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class); + mHasDashCharger = mContext.getResources().getBoolean( + com.android.internal.R.bool.config_hasDashCharger); + mHasWarpCharger = mContext.getResources().getBoolean( + com.android.internal.R.bool.config_hasWarpCharger); + mHasVoocCharger = mContext.getResources().getBoolean( + com.android.internal.R.bool.config_hasVoocCharger) + || mContext.getResources().getBoolean( + com.android.internal.R.bool.config_hasSuperDartCharger); + + mCriticalBatteryLevel = mContext.getResources().getInteger( com.android.internal.R.integer.config_criticalBatteryWarningLevel); mLowBatteryWarningLevel = mContext.getResources().getInteger( @@ -491,6 +516,10 @@ private void processValuesLocked(boolean force) { shutdownIfNoPowerLocked(); shutdownIfOverTempLocked(); + mDashCharger = mHasDashCharger && isDashCharger(); + mWarpCharger = mHasWarpCharger && isWarpCharger(); + mVoocCharger = mHasVoocCharger && isVoocCharger(); + if (force || (mHealthInfo.batteryStatus != mLastBatteryStatus || mHealthInfo.batteryHealth != mLastBatteryHealth @@ -502,7 +531,10 @@ private void processValuesLocked(boolean force) { || mHealthInfo.maxChargingCurrentMicroamps != mLastMaxChargingCurrent || mHealthInfo.maxChargingVoltageMicrovolts != mLastMaxChargingVoltage || mHealthInfo.batteryChargeCounterUah != mLastChargeCounter - || mInvalidCharger != mLastInvalidCharger)) { + || mInvalidCharger != mLastInvalidCharger + || mDashCharger != mLastDashCharger + || mWarpCharger != mLastWarpCharger + || mVoocCharger != mLastVoocCharger)) { if (mPlugType != mLastPlugType) { if (mLastPlugType == BATTERY_PLUGGED_NONE) { @@ -676,6 +708,9 @@ public void run() { mLastChargeCounter = mHealthInfo.batteryChargeCounterUah; mLastBatteryLevelCritical = mBatteryLevelCritical; mLastInvalidCharger = mInvalidCharger; + mLastDashCharger = mDashCharger; + mLastWarpCharger = mWarpCharger; + mLastVoocCharger = mVoocCharger; } } @@ -707,6 +742,9 @@ private void sendBatteryChangedIntentLocked() { BatteryManager.EXTRA_MAX_CHARGING_VOLTAGE, mHealthInfo.maxChargingVoltageMicrovolts); intent.putExtra(BatteryManager.EXTRA_CHARGE_COUNTER, mHealthInfo.batteryChargeCounterUah); + intent.putExtra(BatteryManager.EXTRA_DASH_CHARGER, mDashCharger); + intent.putExtra(BatteryManager.EXTRA_WARP_CHARGER, mWarpCharger); + intent.putExtra(BatteryManager.EXTRA_VOOC_CHARGER, mVoocCharger); if (DEBUG) { Slog.d(TAG, "Sending ACTION_BATTERY_CHANGED. scale:" + BATTERY_SCALE + ", info:" + mHealthInfo.toString()); @@ -761,6 +799,48 @@ private void sendEnqueuedBatteryLevelChangedEvents() { mLastBatteryLevelChangedSentMs = SystemClock.elapsedRealtime(); } + private boolean isDashCharger() { + try { + FileReader file = new FileReader("/sys/class/power_supply/battery/fastchg_status"); + BufferedReader br = new BufferedReader(file); + String state = br.readLine(); + br.close(); + file.close(); + return "1".equals(state); + } catch (FileNotFoundException e) { + } catch (IOException e) { + } + return false; + } + + private boolean isWarpCharger() { + try { + FileReader file = new FileReader("/sys/class/power_supply/battery/fastchg_status"); + BufferedReader br = new BufferedReader(file); + String state = br.readLine(); + br.close(); + file.close(); + return "1".equals(state); + } catch (FileNotFoundException e) { + } catch (IOException e) { + } + return false; + } + + private boolean isVoocCharger() { + try { + FileReader file = new FileReader("/sys/class/power_supply/battery/voocchg_ing"); + BufferedReader br = new BufferedReader(file); + String state = br.readLine(); + br.close(); + file.close(); + return "1".equals(state); + } catch (FileNotFoundException e) { + } catch (IOException e) { + } + return false; + } + // TODO: Current code doesn't work since "--unplugged" flag in BSS was purposefully removed. private void logBatteryStatsLocked() { IBinder batteryInfoService = ServiceManager.getService(BatteryStats.SERVICE_NAME); diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java index dcb7a300b6e9..d20265beb3ce 100644 --- a/services/core/java/com/android/server/StorageManagerService.java +++ b/services/core/java/com/android/server/StorageManagerService.java @@ -1618,6 +1618,8 @@ private void onVolumeCreatedLocked(VolumeInfo vol) { // public API requirement of being in a stable location. if (vol.disk.isAdoptable()) { vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE_FOR_WRITE; + } else if (vol.disk.isSd()) { + vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE_FOR_WRITE; } vol.mountUserId = mCurrentUserId; @@ -2443,8 +2445,10 @@ public void onFinished(int status, PersistableBundle extras) { final long destroy = extras.getLong("destroy"); final DropBoxManager dropBox = mContext.getSystemService(DropBoxManager.class); - dropBox.addText(TAG_STORAGE_BENCHMARK, scrubPath(path) - + " " + ident + " " + create + " " + run + " " + destroy); + if (dropBox != null) { + dropBox.addText(TAG_STORAGE_BENCHMARK, scrubPath(path) + + " " + ident + " " + create + " " + run + " " + destroy); + } synchronized (mLock) { final VolumeRecord rec = findRecordForPath(path); @@ -2606,7 +2610,9 @@ public void onStatus(int status, PersistableBundle extras) { final long time = extras.getLong("time"); final DropBoxManager dropBox = mContext.getSystemService(DropBoxManager.class); - dropBox.addText(TAG_STORAGE_TRIM, scrubPath(path) + " " + bytes + " " + time); + if (dropBox != null) { + dropBox.addText(TAG_STORAGE_TRIM, scrubPath(path) + " " + bytes + " " + time); + } synchronized (mLock) { final VolumeRecord rec = findRecordForPath(path); @@ -4505,6 +4511,10 @@ private int getMountModeInternal(int uid, String packageName) { return StorageManager.MOUNT_MODE_EXTERNAL_PASS_THROUGH; } + if (Arrays.asList(packagesForUid).contains("com.android.externalstorage")) { + return StorageManager.MOUNT_MODE_EXTERNAL_PASS_THROUGH; + } + if ((mDownloadsAuthorityAppId == UserHandle.getAppId(uid) || mExternalStorageAuthorityAppId == UserHandle.getAppId(uid))) { // DownloadManager can write in app-private directories on behalf of apps; diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java index 6b731c319c4b..4f1efd627e40 100644 --- a/services/core/java/com/android/server/accounts/AccountManagerService.java +++ b/services/core/java/com/android/server/accounts/AccountManagerService.java @@ -89,6 +89,7 @@ import android.os.UserManager; import android.stats.devicepolicy.DevicePolicyEnums; import android.text.TextUtils; +import android.util.EventLog; import android.util.Log; import android.util.Pair; import android.util.Slog; @@ -527,7 +528,7 @@ public Map getAccountsAndVisibilityForPackage(String packageNa private Map getAccountsAndVisibilityForPackage(String packageName, List accountTypes, Integer callingUid, UserAccounts accounts) { if (!packageExistsForUser(packageName, accounts.userId)) { - Log.d(TAG, "Package not found " + packageName); + Log.w(TAG, "getAccountsAndVisibilityForPackage#Package not found " + packageName); return new LinkedHashMap<>(); } @@ -676,7 +677,7 @@ private Integer resolveAccountVisibility(Account account, @NonNull String packag restoreCallingIdentity(identityToken); } } catch (NameNotFoundException e) { - Log.d(TAG, "Package not found " + e.getMessage()); + Log.w(TAG, "resolveAccountVisibility#Package not found " + e.getMessage()); return AccountManager.VISIBILITY_NOT_VISIBLE; } @@ -755,7 +756,7 @@ private boolean isPreOApplication(String packageName) { } return true; } catch (NameNotFoundException e) { - Log.d(TAG, "Package not found " + e.getMessage()); + Log.w(TAG, "isPreOApplication#Package not found " + e.getMessage()); return true; } } @@ -3100,7 +3101,7 @@ public void onResult(Bundle result) { */ if (!checkKeyIntent( Binder.getCallingUid(), - intent)) { + result)) { onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, "invalid intent in bundle returned"); return; @@ -3519,7 +3520,7 @@ public void onResult(Bundle result) { && (intent = result.getParcelable(AccountManager.KEY_INTENT)) != null) { if (!checkKeyIntent( Binder.getCallingUid(), - intent)) { + result)) { onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, "invalid intent in bundle returned"); return; @@ -4062,7 +4063,7 @@ public boolean hasAccountAccess(@NonNull Account account, @NonNull String packa int uid = mPackageManager.getPackageUidAsUser(packageName, userId); return hasAccountAccess(account, packageName, uid); } catch (NameNotFoundException e) { - Log.d(TAG, "Package not found " + e.getMessage()); + Log.w(TAG, "hasAccountAccess#Package not found " + e.getMessage()); return false; } } @@ -4194,7 +4195,7 @@ public boolean someUserHasAccount(@NonNull final Account account) { } final long token = Binder.clearCallingIdentity(); try { - AccountAndUser[] allAccounts = getAllAccounts(); + AccountAndUser[] allAccounts = getAllAccountsForSystemProcess(); for (int i = allAccounts.length - 1; i >= 0; i--) { if (allAccounts[i].account.equals(account)) { return true; @@ -4344,10 +4345,11 @@ public Account[] getAccounts(int userId, String opPackageName) { /** * Returns accounts for all running users, ignores visibility values. * + * Should only be called by System process. * @hide */ @NonNull - public AccountAndUser[] getRunningAccounts() { + public AccountAndUser[] getRunningAccountsForSystem() { final int[] runningUserIds; try { runningUserIds = ActivityManager.getService().getRunningUserIds(); @@ -4355,26 +4357,34 @@ public AccountAndUser[] getRunningAccounts() { // Running in system_server; should never happen throw new RuntimeException(e); } - return getAccounts(runningUserIds); + return getAccountsForSystem(runningUserIds); } /** * Returns accounts for all users, ignores visibility values. * + * Should only be called by system process + * * @hide */ @NonNull - public AccountAndUser[] getAllAccounts() { + public AccountAndUser[] getAllAccountsForSystemProcess() { final List users = getUserManager().getAliveUsers(); final int[] userIds = new int[users.size()]; for (int i = 0; i < userIds.length; i++) { userIds[i] = users.get(i).id; } - return getAccounts(userIds); + return getAccountsForSystem(userIds); } + /** + * Returns all accounts for the given user, ignores all visibility checks. + * This should only be called by system process. + * + * @hide + */ @NonNull - private AccountAndUser[] getAccounts(int[] userIds) { + private AccountAndUser[] getAccountsForSystem(int[] userIds) { final ArrayList runningAccounts = Lists.newArrayList(); for (int userId : userIds) { UserAccounts userAccounts = getUserAccounts(userId); @@ -4383,7 +4393,7 @@ private AccountAndUser[] getAccounts(int[] userIds) { userAccounts, null /* type */, Binder.getCallingUid(), - null /* packageName */, + "android"/* packageName */, false /* include managed not visible*/); for (Account account : accounts) { runningAccounts.add(new AccountAndUser(account, userId)); @@ -4870,7 +4880,13 @@ IAccountManagerResponse getResponseAndClose() { * into launching arbitrary intents on the device via by tricking to click authenticator * supplied entries in the system Settings app. */ - protected boolean checkKeyIntent(int authUid, Intent intent) { + protected boolean checkKeyIntent(int authUid, Bundle bundle) { + if (!checkKeyIntentParceledCorrectly(bundle)) { + EventLog.writeEvent(0x534e4554, "250588548", authUid, ""); + return false; + } + + Intent intent = bundle.getParcelable(AccountManager.KEY_INTENT, Intent.class); // Explicitly set an empty ClipData to ensure that we don't offer to // promote any Uris contained inside for granting purposes if (intent.getClipData() == null) { @@ -4905,6 +4921,25 @@ protected boolean checkKeyIntent(int authUid, Intent intent) { } } + /** + * Simulate the client side's deserialization of KEY_INTENT value, to make sure they don't + * violate our security policy. + * + * In particular we want to make sure the Authenticator doesn't trick users + * into launching arbitrary intents on the device via exploiting any other Parcel read/write + * mismatch problems. + */ + private boolean checkKeyIntentParceledCorrectly(Bundle bundle) { + Parcel p = Parcel.obtain(); + p.writeBundle(bundle); + p.setDataPosition(0); + Bundle simulateBundle = p.readBundle(); + p.recycle(); + Intent intent = bundle.getParcelable(AccountManager.KEY_INTENT, Intent.class); + return (intent.filterEquals(simulateBundle.getParcelable(AccountManager.KEY_INTENT, + Intent.class))); + } + private boolean isExportedSystemActivity(ActivityInfo activityInfo) { String className = activityInfo.name; return "android".equals(activityInfo.packageName) && @@ -5051,7 +5086,7 @@ public void onResult(Bundle result) { && (intent = result.getParcelable(AccountManager.KEY_INTENT)) != null) { if (!checkKeyIntent( Binder.getCallingUid(), - intent)) { + result)) { onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, "invalid intent in bundle returned"); return; @@ -5329,7 +5364,7 @@ private void dumpUser(UserAccounts userAccounts, FileDescriptor fd, PrintWriter } } else { Account[] accounts = getAccountsFromCache(userAccounts, null /* type */, - Process.SYSTEM_UID, null /* packageName */, false); + Process.SYSTEM_UID, "android" /* packageName */, false); fout.println("Accounts: " + accounts.length); for (Account account : accounts) { fout.println(" " + account.toString()); @@ -5524,7 +5559,7 @@ private boolean isPrivileged(int callingUid) { return true; } } catch (PackageManager.NameNotFoundException e) { - Log.d(TAG, "Package not found " + e.getMessage()); + Log.w(TAG, "isPrivileged#Package not found " + e.getMessage()); } } } finally { @@ -6048,7 +6083,7 @@ private Map filterSharedAccounts(UserAccounts userAccounts, } } } catch (NameNotFoundException e) { - Log.d(TAG, "Package not found " + e.getMessage()); + Log.w(TAG, "filterSharedAccounts#Package not found " + e.getMessage()); } Map filtered = new LinkedHashMap<>(); for (Map.Entry entry : unfiltered.entrySet()) { diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 9840e0ff90ce..a96524823421 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -148,6 +148,7 @@ import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; +import android.util.BoostFramework; import android.util.EventLog; import android.util.Pair; import android.util.PrintWriterPrinter; @@ -175,6 +176,7 @@ import com.android.server.am.ActivityManagerService.ItemMatcher; import com.android.server.am.LowMemDetector.MemFactor; import com.android.server.uri.NeededUriGrants; +import com.android.server.wm.ActivityRecord; import com.android.server.wm.ActivityServiceConnectionsHolder; import java.io.FileDescriptor; @@ -250,6 +252,12 @@ public final class ActiveServices { // at the same time. final int mMaxStartingBackground; + //mPerf Object + public static BoostFramework mPerf = new BoostFramework(); + + // Flag to reschedule the services during app launch. Disable by default. + private static boolean SERVICE_RESCHEDULE = false; + /** * Master service bookkeeping, keyed by user number. */ @@ -329,6 +337,9 @@ public final class ActiveServices { /** Amount of time to allow a last ANR message to exist before freeing the memory. */ static final int LAST_ANR_LIFETIME_DURATION_MSECS = 2 * 60 * 60 * 1000; // Two hours + private final boolean isLowRamDevice = + SystemProperties.getBoolean("ro.config.low_ram", false); + String mLastAnrDump; AppWidgetManagerInternal mAppWidgetManagerInternal; @@ -568,6 +579,9 @@ public ActiveServices(ActivityManagerService service) { ? maxBg : ActivityManager.isLowRamDeviceStatic() ? 1 : 8; final IBinder b = ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE); + + if(mPerf != null) + SERVICE_RESCHEDULE = Boolean.parseBoolean(mPerf.perfGetProp("ro.vendor.qti.am.reschedule_service", "false")); } void systemServicesReady() { @@ -3731,6 +3745,14 @@ private final boolean scheduleServiceRestartLocked(ServiceRecord r, boolean allo r.pendingStarts.add(0, si); long dur = SystemClock.uptimeMillis() - si.deliveredTime; dur *= 2; + if (SERVICE_RESCHEDULE && DEBUG_DELAYED_SERVICE) { + Slog.w(TAG,"Can add more delay !!!" + +" si.deliveredTime "+si.deliveredTime + +" dur "+dur + +" si.deliveryCount "+si.deliveryCount + +" si.doneExecutingCount "+si.doneExecutingCount + +" allowCancel "+allowCancel); + } if (minDuration < dur) minDuration = dur; if (resetTime < dur) resetTime = dur; } else { @@ -3754,6 +3776,13 @@ private final boolean scheduleServiceRestartLocked(ServiceRecord r, boolean allo } r.totalRestartCount++; + if (SERVICE_RESCHEDULE && DEBUG_DELAYED_SERVICE) { + Slog.w(TAG,"r.name "+r.name+" N "+N+" minDuration "+minDuration + +" resetTime "+resetTime+" now "+now + +" r.restartDelay "+r.restartDelay + +" r.restartTime+resetTime "+(r.restartTime+resetTime) + +" allowCancel "+allowCancel); + } if (r.restartDelay == 0) { r.restartCount++; r.restartDelay = minDuration; @@ -3779,6 +3808,14 @@ private final boolean scheduleServiceRestartLocked(ServiceRecord r, boolean allo if (isServiceRestartBackoffEnabledLocked(r.packageName)) { r.nextRestartTime = r.mEarliestRestartTime = now + r.restartDelay; + if (SERVICE_RESCHEDULE && DEBUG_DELAYED_SERVICE) { + Slog.w(TAG,"r.name "+r.name+" N "+N+" minDuration "+minDuration + +" resetTime "+resetTime+" now "+now + +" r.restartDelay "+r.restartDelay + +" r.restartTime+resetTime "+(r.restartTime+resetTime) + +" r.nextRestartTime "+r.nextRestartTime + +" allowCancel "+allowCancel); + } if (inRestarting) { // Take it out of the list temporarily for easier maintenance of the list. @@ -3882,6 +3919,15 @@ void performScheduleRestartLocked(ServiceRecord r, @NonNull String scheduling, r.nextRestartTime = now + r.restartDelay; Slog.w(TAG, scheduling + " restart of crashed service " + r.shortInstanceName + " in " + r.restartDelay + "ms for " + reason); + + if (SERVICE_RESCHEDULE && DEBUG_DELAYED_SERVICE) { + for (int i=mRestartingServices.size()-1; i>=0; i--) { + ServiceRecord r2 = mRestartingServices.get(i); + Slog.w(TAG,"Restarting list - i "+i+" r2.nextRestartTime " + +r2.nextRestartTime+" r2.name "+r2.name); + } + } + EventLog.writeEvent(EventLogTags.AM_SCHEDULE_SERVICE_RESTART, r.userId, r.shortInstanceName, r.restartDelay); } @@ -4055,8 +4101,31 @@ final void performServiceRestartLocked(ServiceRecord r) { return; } try { - bringUpServiceLocked(r, r.intent.getIntent().getFlags(), r.createdFromFg, true, false, - false, true); + if(SERVICE_RESCHEDULE) { + boolean shouldDelay = false; + ActivityRecord top_rc = mAm.mTaskSupervisor.getTopResumedActivity(); + + boolean isPersistent + = !((r.serviceInfo.applicationInfo.flags&ApplicationInfo.FLAG_PERSISTENT) == 0); + if(top_rc != null) { + if(top_rc.launching && !r.shortInstanceName.contains(top_rc.packageName) + && !isPersistent && r.isForeground == false) { + shouldDelay = true; + } + } + if(!shouldDelay) { + bringUpServiceLocked(r, r.intent.getIntent().getFlags(), r.createdFromFg, true, false, false, true); + } else { + if (DEBUG_DELAYED_SERVICE) { + Slog.v(TAG, "Reschedule service restart due to app launch" + +" r.shortInstanceName "+r.shortInstanceName+" r.app = "+r.app); + } + r.resetRestartCounter(); + scheduleServiceRestartLocked(r, true); + } + } else { + bringUpServiceLocked(r, r.intent.getIntent().getFlags(), r.createdFromFg, true, false, false, true); + } } catch (TransactionTooLargeException e) { // Ignore, it's been logged and nothing upstack cares. } finally { @@ -4219,7 +4288,8 @@ private String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean ex final String procName = r.processName; HostingRecord hostingRecord = new HostingRecord( HostingRecord.HOSTING_TYPE_SERVICE, r.instanceName, - r.definingPackageName, r.definingUid, r.serviceInfo.processName); + r.definingPackageName, r.definingUid, r.serviceInfo.processName, + getHostingRecordTriggerType(r)); ProcessRecord app; if (!isolated) { @@ -4323,6 +4393,14 @@ private String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean ex return null; } + private String getHostingRecordTriggerType(ServiceRecord r) { + if (Manifest.permission.BIND_JOB_SERVICE.equals(r.permission) + && r.mRecentCallingUid == SYSTEM_UID) { + return HostingRecord.TRIGGER_TYPE_JOB; + } + return HostingRecord.TRIGGER_TYPE_UNKNOWN; + } + private final void requestServiceBindingsLocked(ServiceRecord r, boolean execInFg) throws TransactionTooLargeException { for (int i=r.bindings.size()-1; i>=0; i--) { @@ -4384,6 +4462,7 @@ private void realStartServiceLocked(ServiceRecord r, ProcessRecord app, app.mState.getReportedProcState()); r.postNotification(); created = true; + } catch (DeadObjectException e) { Slog.w(TAG, "Application dead when creating service " + r); mAm.appDiedLocked(app, "Died when creating service"); @@ -4397,7 +4476,12 @@ private void realStartServiceLocked(ServiceRecord r, ProcessRecord app, // Cleanup. if (newService) { psr.stopService(r); - r.setProcess(null, null, 0, null); + r.app = null; + if (SERVICE_RESCHEDULE && DEBUG_DELAYED_SERVICE) { + Slog.w(TAG, " Failed to create Service !!!! ." + +"This will introduce huge delay... " + +r.shortInstanceName + " in " + r.restartDelay + "ms"); + } } // Retry. @@ -4578,7 +4662,6 @@ private final void bringDownServiceIfNeededLocked(ServiceRecord r, boolean knowC private void bringDownServiceLocked(ServiceRecord r, boolean enqueueOomAdj) { //Slog.i(TAG, "Bring down service:"); //r.dump(" "); - // Report to all of the connections that the service is no longer // available. ArrayMap> connections = r.getConnections(); diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java index c3720965e7f6..b893a3847e3a 100644 --- a/services/core/java/com/android/server/am/ActivityManagerConstants.java +++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java @@ -32,15 +32,18 @@ import android.net.Uri; import android.os.Build; import android.os.Handler; +import android.os.Process; +import android.os.SystemClock; +import android.os.SystemProperties; import android.os.Message; import android.os.PowerExemptionManager; -import android.os.SystemClock; import android.provider.DeviceConfig; import android.provider.DeviceConfig.OnPropertiesChangedListener; import android.provider.DeviceConfig.Properties; import android.provider.Settings; import android.text.TextUtils; import android.util.ArraySet; +import android.util.BoostFramework; import android.util.KeyValueListParser; import android.util.Slog; @@ -145,7 +148,7 @@ final class ActivityManagerConstants extends ContentObserver { */ static final String KEY_NETWORK_ACCESS_TIMEOUT_MS = "network_access_timeout_ms"; - private static final int DEFAULT_MAX_CACHED_PROCESSES = 32; + private static int DEFAULT_MAX_CACHED_PROCESSES = 32; private static final long DEFAULT_FGSERVICE_MIN_SHOWN_TIME = 2*1000; private static final long DEFAULT_FGSERVICE_MIN_REPORT_TIME = 3*1000; private static final long DEFAULT_FGSERVICE_SCREEN_ON_BEFORE_TIME = 1*1000; @@ -705,6 +708,15 @@ final class ActivityManagerConstants extends ContentObserver { // process limit. This will be initialized in the constructor. public int CUR_MAX_CACHED_PROCESSES; + public static BoostFramework mPerf = new BoostFramework(); + + static boolean USE_TRIM_SETTINGS = true; + static int EMPTY_APP_PERCENT = 50; + static int TRIM_EMPTY_PERCENT = 100; + static int TRIM_CACHE_PERCENT = 100; + static long TRIM_ENABLE_MEMORY = 1073741824; + public static boolean allowTrim() { return Process.getTotalMemory() < TRIM_ENABLE_MEMORY ; } + // The maximum number of empty app processes we will let sit around. This will be // initialized in the constructor. public int CUR_MAX_EMPTY_PROCESSES; @@ -1066,6 +1078,28 @@ public void onPropertiesChanged(Properties properties) { CUR_MAX_EMPTY_PROCESSES = computeEmptyProcessLimit(CUR_MAX_CACHED_PROCESSES); } + private void updatePerfConfigConstants() { + if (mPerf != null) { + // Maximum number of cached processes we will allow. + DEFAULT_MAX_CACHED_PROCESSES = MAX_CACHED_PROCESSES = CUR_MAX_CACHED_PROCESSES = Integer.valueOf( + mPerf.perfGetProp("ro.vendor.qti.sys.fw.bg_apps_limit", "32")); + + //Trim Settings + USE_TRIM_SETTINGS = Boolean.parseBoolean(mPerf.perfGetProp("ro.vendor.qti.sys.fw.use_trim_settings", "true")); + EMPTY_APP_PERCENT = Integer.valueOf(mPerf.perfGetProp("ro.vendor.qti.sys.fw.empty_app_percent", "50")); + TRIM_EMPTY_PERCENT = Integer.valueOf(mPerf.perfGetProp("ro.vendor.qti.sys.fw.trim_empty_percent", "100")); + TRIM_CACHE_PERCENT = Integer.valueOf(mPerf.perfGetProp("ro.vendor.qti.sys.fw.trim_cache_percent", "100")); + TRIM_ENABLE_MEMORY = Long.valueOf(mPerf.perfGetProp("ro.vendor.qti.sys.fw.trim_enable_memory", "1073741824")); + + // The maximum number of empty app processes we will let sit around. + CUR_MAX_EMPTY_PROCESSES = computeEmptyProcessLimit(CUR_MAX_CACHED_PROCESSES); + + final int rawEmptyProcesses = computeEmptyProcessLimit(MAX_CACHED_PROCESSES); + CUR_TRIM_EMPTY_PROCESSES = computeTrimEmptyApps(rawEmptyProcesses); + CUR_TRIM_CACHED_PROCESSES = computeTrimCachedApps(rawEmptyProcesses, MAX_CACHED_PROCESSES); + } + } + public void start(ContentResolver resolver) { mResolver = resolver; mResolver.registerContentObserver(ACTIVITY_MANAGER_CONSTANTS_URI, false, this); @@ -1077,6 +1111,8 @@ public void start(ContentResolver resolver) { false, this); } updateConstants(); + updatePerfConfigConstants(); + if (mSystemServerAutomaticHeapDumpEnabled) { updateEnableAutomaticSystemServerHeapDumps(); } @@ -1111,7 +1147,27 @@ public int getOverrideMaxCachedProcesses() { } public static int computeEmptyProcessLimit(int totalProcessLimit) { - return totalProcessLimit/2; + if(USE_TRIM_SETTINGS && allowTrim()) { + return totalProcessLimit*EMPTY_APP_PERCENT/100; + } else { + return totalProcessLimit/2; + } + } + + public static int computeTrimEmptyApps(int rawMaxEmptyProcesses) { + if (USE_TRIM_SETTINGS && allowTrim()) { + return rawMaxEmptyProcesses*TRIM_EMPTY_PERCENT/100; + } else { + return rawMaxEmptyProcesses/2; + } + } + + public static int computeTrimCachedApps(int rawMaxEmptyProcesses, int totalProcessLimit) { + if (USE_TRIM_SETTINGS && allowTrim()) { + return totalProcessLimit*TRIM_CACHE_PERCENT/100; + } else { + return (totalProcessLimit-rawMaxEmptyProcesses)/3; + } } @Override @@ -1588,8 +1644,9 @@ private void updateMaxCachedProcesses() { // to consider the same level the point where we do trimming regardless of any // additional enforced limit. final int rawMaxEmptyProcesses = computeEmptyProcessLimit(MAX_CACHED_PROCESSES); - CUR_TRIM_EMPTY_PROCESSES = rawMaxEmptyProcesses/2; - CUR_TRIM_CACHED_PROCESSES = (MAX_CACHED_PROCESSES-rawMaxEmptyProcesses)/3; + CUR_TRIM_EMPTY_PROCESSES = computeTrimEmptyApps(rawMaxEmptyProcesses); + CUR_TRIM_CACHED_PROCESSES = + computeTrimCachedApps(rawMaxEmptyProcesses, MAX_CACHED_PROCESSES); } private void updateMinAssocLogDuration() { diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index d4b760f3866f..7d4468440127 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -334,6 +334,8 @@ import android.view.LayoutInflater; import android.view.View; import android.view.WindowManager; +import android.util.BoostFramework; + import android.view.autofill.AutofillManagerInternal; import com.android.internal.annotations.CompositeRWLock; @@ -346,6 +348,7 @@ import com.android.internal.app.SystemUserHomeActivity; import com.android.internal.app.procstats.ProcessState; import com.android.internal.app.procstats.ProcessStats; +import com.android.internal.app.ActivityTrigger; import com.android.internal.content.InstallLocationUtils; import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; import com.android.internal.notification.SystemNotificationChannels; @@ -369,12 +372,10 @@ import com.android.internal.util.FrameworkStatsLog; import com.android.internal.util.MemInfoReader; import com.android.internal.util.Preconditions; -import com.android.internal.util.function.DecFunction; import com.android.internal.util.function.HeptFunction; import com.android.internal.util.function.HexFunction; import com.android.internal.util.function.QuadFunction; import com.android.internal.util.function.QuintFunction; -import com.android.internal.util.function.TriFunction; import com.android.internal.util.function.UndecFunction; import com.android.server.AlarmManagerInternal; import com.android.server.DeviceIdleInternal; @@ -420,6 +421,7 @@ import com.android.server.vr.VrManagerInternal; import com.android.server.wm.ActivityMetricsLaunchObserver; import com.android.server.wm.ActivityServiceConnectionsHolder; +import com.android.server.wm.ActivityTaskSupervisor; import com.android.server.wm.ActivityTaskManagerInternal; import com.android.server.wm.ActivityTaskManagerService; import com.android.server.wm.WindowManagerInternal; @@ -563,6 +565,12 @@ public class ActivityManagerService extends IActivityManager.Stub 2000 * Build.HW_TIMEOUT_MULTIPLIER; // 2 seconds; private static final int JAVA_DUMP_MINIMUM_SIZE = 100; // 100 bytes. + /* Freq Aggr boost objects */ + public static BoostFramework mPerfServiceStartHint = null; + /* UX perf event object */ + public static BoostFramework mUxPerf = new BoostFramework(); + public static boolean mForceStopKill = false; + OomAdjuster mOomAdjuster; static final String EXTRA_TITLE = "android.intent.extra.TITLE"; @@ -592,6 +600,9 @@ public class ActivityManagerService extends IActivityManager.Stub private Installer mInstaller; + /** Run all ActivityStacks through this */ + ActivityTaskSupervisor mTaskSupervisor; + final InstrumentationReporter mInstrumentationReporter = new InstrumentationReporter(); @CompositeRWLock({"this", "mProcLock"}) @@ -1557,6 +1568,10 @@ public void binderDied() { static final HostingRecord sNullHostingRecord = new HostingRecord(HostingRecord.HOSTING_TYPE_EMPTY); + + final SwipeToScreenshotObserver mSwipeToScreenshotObserver; + private boolean mIsSwipeToScreenshotEnabled; + /** * Used to notify activity lifecycle events. */ @@ -2344,6 +2359,7 @@ AppOpsManager getAppOpsManager() { mFgBroadcastQueue = mBgBroadcastQueue = mBgOffloadBroadcastQueue = mFgOffloadBroadcastQueue = null; mComponentAliasResolver = new ComponentAliasResolver(this); + mSwipeToScreenshotObserver = null; } // Note: This method is invoked on the main thread but may need to attach various @@ -2452,6 +2468,7 @@ public ActivityManagerService(Context systemContext, ActivityTaskManagerService mActivityTaskManager.initialize(mIntentFirewall, mPendingIntentController, DisplayThread.get().getLooper()); mAtmInternal = LocalServices.getService(ActivityTaskManagerInternal.class); + mTaskSupervisor = mActivityTaskManager.mTaskSupervisor; mHiddenApiBlacklist = new HiddenApiSettings(mHandler, mContext); mSdkSandboxSettings = new SdkSandboxSettings(mContext); @@ -2468,7 +2485,7 @@ public ActivityManagerService(Context systemContext, ActivityTaskManagerService Process.THREAD_GROUP_SYSTEM); Process.setThreadGroupAndCpuset( mOomAdjuster.mCachedAppOptimizer.mCachedAppOptimizerThread.getThreadId(), - Process.THREAD_GROUP_SYSTEM); + mOomAdjuster.mCachedAppOptimizer.mCompactionPriority); } catch (Exception e) { Slog.w(TAG, "Setting background thread cpuset failed"); } @@ -2477,6 +2494,7 @@ public ActivityManagerService(Context systemContext, ActivityTaskManagerService mPendingStartActivityUids = new PendingStartActivityUids(); mTraceErrorLogger = new TraceErrorLogger(); mComponentAliasResolver = new ComponentAliasResolver(this); + mSwipeToScreenshotObserver = new SwipeToScreenshotObserver(mHandler, mContext); } public void setSystemServiceManager(SystemServiceManager mgr) { @@ -3155,6 +3173,46 @@ public final int startActivityFromRecents(int taskId, Bundle bOptions) { return mActivityTaskManager.startActivityFromRecents(taskId, bOptions); } + public int startActivityAsUserEmpty(Bundle options) { + ArrayList pApps = options.getStringArrayList("start_empty_apps"); + if (pApps != null && pApps.size() > 0) { + Iterator apps_itr = pApps.iterator(); + while (apps_itr.hasNext()) { + ProcessRecord empty_app = null; + String app_str = apps_itr.next(); + if (app_str == null) + continue; + synchronized (this) { + Intent intent_l = null; + try { + intent_l = mContext.getPackageManager().getLaunchIntentForPackage(app_str); + if (intent_l == null) + continue; + ActivityInfo aInfo = mTaskSupervisor.resolveActivity(intent_l, null, + 0, null, 0, 0); + if (aInfo == null) + continue; + empty_app = startProcessLocked( + app_str, + aInfo.applicationInfo, + false /* knownToBeDead */, + 0 /* intentFlags */, + sNullHostingRecord /* hostingRecord */, + ZYGOTE_POLICY_FLAG_EMPTY /* zygotePolicyFlags */, + false /* allowWhileBooting */, + false /* isolated */); + if (empty_app != null) + updateOomAdjLocked(empty_app, OomAdjuster.OOM_ADJ_REASON_NONE); + } catch (Exception e) { + if (DEBUG_PROCESSES) + Slog.w(TAG, "Exception raised trying to start app as empty " + e); + } + } + } + } + return 1; + } + /** * This is the internal entry point for handling Activity.finish(). * @@ -3330,6 +3388,15 @@ final void appDiedLocked(ProcessRecord app, int pid, IApplicationThread thread, mAppProfiler.setAllowLowerMemLevelLocked(false); doLowMem = false; } + + if (mUxPerf != null && !mForceStopKill && !app.mErrorState.isNotResponding() && !app.mErrorState.isCrashing()) { + if (mUxPerf.board_first_api_lvl < BoostFramework.VENDOR_T_API_LEVEL && + mUxPerf.board_api_lvl < BoostFramework.VENDOR_T_API_LEVEL) { + mUxPerf.perfUXEngine_events(BoostFramework.UXE_EVENT_KILL, 0, app.processName, 0); + } + mUxPerf.perfEvent(BoostFramework.VENDOR_HINT_KILL, app.processName, 2, 0, pid); + } + EventLogTags.writeAmProcDied(app.userId, pid, app.processName, setAdj, setProcState); if (DEBUG_CLEANUP) Slog.v(TAG_CLEANUP, "Dying app: " + app + ", pid: " + pid + ", thread: " + thread.asBinder()); @@ -3752,22 +3819,27 @@ public void onRemoveCompleted(String packageName, boolean succeeded) finishForceStopPackageLocked(packageName, appInfo.uid); } } - final Intent intent = new Intent(Intent.ACTION_PACKAGE_DATA_CLEARED, - Uri.fromParts("package", packageName, null)); - intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); - intent.putExtra(Intent.EXTRA_UID, (appInfo != null) ? appInfo.uid : -1); - intent.putExtra(Intent.EXTRA_USER_HANDLE, resolvedUserId); - final int[] visibilityAllowList = - mPackageManagerInt.getVisibilityAllowList(packageName, resolvedUserId); - if (isInstantApp) { - intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName); - broadcastIntentInPackage("android", null, SYSTEM_UID, uid, pid, intent, - null, null, 0, null, null, permission.ACCESS_INSTANT_APPS, null, - false, false, resolvedUserId, false, null, visibilityAllowList); - } else { - broadcastIntentInPackage("android", null, SYSTEM_UID, uid, pid, intent, - null, null, 0, null, null, null, null, false, false, resolvedUserId, - false, null, visibilityAllowList); + + if (succeeded) { + final Intent intent = new Intent(Intent.ACTION_PACKAGE_DATA_CLEARED, + Uri.fromParts("package", packageName, null /* fragment */)); + intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); + intent.putExtra(Intent.EXTRA_UID, + (appInfo != null) ? appInfo.uid : INVALID_UID); + intent.putExtra(Intent.EXTRA_USER_HANDLE, resolvedUserId); + if (isInstantApp) { + intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName); + } + final int[] visibilityAllowList = mPackageManagerInt.getVisibilityAllowList( + packageName, resolvedUserId); + + broadcastIntentInPackage("android", null /* featureId */, SYSTEM_UID, + uid, pid, intent, null /* resolvedType */, null /* resultTo */, + 0 /* resultCode */, null /* resultData */, null /* resultExtras */, + isInstantApp ? permission.ACCESS_INSTANT_APPS : null, + null /* bOptions */, false /* serialized */, false /* sticky */, + resolvedUserId, false /* allowBackgroundActivityStarts */, + null /* backgroundActivityStartsToken */, visibilityAllowList); } if (observer != null) { @@ -4425,6 +4497,7 @@ void stopAppForUserInternal(final String packageName, @UserIdInt final int userI // A specific subset of the work done in forceStopPackageLocked(), because we are // intentionally not rendering the app nonfunctional; we're just halting its current // execution. + mForceStopKill = true; final int appId = UserHandle.getAppId(uid); synchronized (this) { synchronized (mProcLock) { @@ -4473,6 +4546,7 @@ final boolean forceStopPackageLocked(String packageName, int appId, mAppErrors.resetProcessCrashTime(packageName == null, appId, userId); } + mForceStopKill = true; synchronized (mProcLock) { // Notify first that the package is stopped, so its process won't be restarted @@ -4731,6 +4805,19 @@ private boolean attachApplicationLocked(@NonNull IApplicationThread thread, EventLogTags.writeAmProcBound(app.userId, pid, app.processName); + if (mUxPerf != null && app.getHostingRecord() != null && app.getHostingRecord().isTopApp()) { + if (mUxPerf.getPerfHalVersion() >= BoostFramework.PERF_HAL_V23) { + int pkgType = mUxPerf.perfGetFeedback( + BoostFramework.VENDOR_FEEDBACK_WORKLOAD_TYPE, app.processName); + mUxPerf.perfHintAcqRel(-1, + BoostFramework.VENDOR_HINT_FIRST_LAUNCH_BOOST, app.processName, + pid, BoostFramework.Launch.TYPE_ATTACH_APPLICATION, 1, pkgType); + } else { + mUxPerf.perfHint(BoostFramework.VENDOR_HINT_FIRST_LAUNCH_BOOST, app.processName, + pid, BoostFramework.Launch.TYPE_ATTACH_APPLICATION); + } + } + synchronized (mProcLock) { app.mState.setCurAdj(ProcessList.INVALID_ADJ); app.mState.setSetAdj(ProcessList.INVALID_ADJ); @@ -5008,7 +5095,8 @@ private boolean attachApplicationLocked(@NonNull IApplicationThread thread, hostingRecord.getType(), hostingRecord.getName(), shortAction, - HostingRecord.getHostingTypeIdStatsd(hostingRecord.getType())); + HostingRecord.getHostingTypeIdStatsd(hostingRecord.getType()), + HostingRecord.getTriggerTypeForStatsd(hostingRecord.getTriggerType())); return true; } @@ -5152,6 +5240,7 @@ public void performReceive(Intent intent, int resultCode, String data, Bundle extras, boolean ordered, boolean sticky, int sendingUser) { synchronized (mProcLock) { + mOomAdjuster.mCachedAppOptimizer.compactAllSystem(); mAppProfiler.requestPssAllProcsLPr( SystemClock.uptimeMillis(), true, false); } @@ -8022,6 +8111,7 @@ private void retrieveSettings() { com.android.internal.R.bool.config_multiuserDelayUserDataLocking); mUserController.setInitialConfig(userSwitchUiEnabled, maxRunningUsers, delayUserDataLocking); + mSwipeToScreenshotObserver.registerObserver(); } mAppErrors.loadAppsNotReportingCrashesFromConfig(res.getString( com.android.internal.R.string.config_appsNotReportingCrashes)); @@ -12442,6 +12532,28 @@ public void run(){ } app.setPid(0); } + + // Call Preferred App + if (app != null) { + ArrayList results = new ArrayList(); + mProcessList.mAppExitInfoTracker.getExitInfo( + app.processName, app.uid, app.getPid(), 0, results); + if (results != null) { + boolean recentAppClose = false; + for (int i=0; i procs = new ArrayList<>(); @@ -18315,19 +18432,20 @@ public SyncNotedAppOp startOperation(IBinder token, int code, int uid, } @Override - public SyncNotedAppOp startProxyOperation(int code, + public SyncNotedAppOp startProxyOperation(@NonNull IBinder clientId, int code, @NonNull AttributionSource attributionSource, boolean startIfModeDefault, boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage, boolean skipProxyOperation, @AttributionFlags int proxyAttributionFlags, @AttributionFlags int proxiedAttributionFlags, int attributionChainId, - @NonNull DecFunction superImpl) { + @NonNull UndecFunction superImpl) { if (attributionSource.getUid() == mTargetUid && isTargetOp(code)) { final int shellUid = UserHandle.getUid(UserHandle.getUserId( attributionSource.getUid()), Process.SHELL_UID); final long identity = Binder.clearCallingIdentity(); try { - return superImpl.apply(code, new AttributionSource(shellUid, + return superImpl.apply(clientId, code, new AttributionSource(shellUid, "com.android.shell", attributionSource.getAttributionTag(), attributionSource.getToken(), attributionSource.getNext()), startIfModeDefault, shouldCollectAsyncNotedOp, message, @@ -18337,21 +18455,22 @@ public SyncNotedAppOp startProxyOperation(int code, Binder.restoreCallingIdentity(identity); } } - return superImpl.apply(code, attributionSource, startIfModeDefault, + return superImpl.apply(clientId, code, attributionSource, startIfModeDefault, shouldCollectAsyncNotedOp, message, shouldCollectMessage, skipProxyOperation, proxyAttributionFlags, proxiedAttributionFlags, attributionChainId); } @Override - public void finishProxyOperation(int code, @NonNull AttributionSource attributionSource, - boolean skipProxyOperation, @NonNull TriFunction superImpl) { + public void finishProxyOperation(@NonNull IBinder clientId, int code, + @NonNull AttributionSource attributionSource, boolean skipProxyOperation, + @NonNull QuadFunction superImpl) { if (attributionSource.getUid() == mTargetUid && isTargetOp(code)) { final int shellUid = UserHandle.getUid(UserHandle.getUserId( attributionSource.getUid()), Process.SHELL_UID); final long identity = Binder.clearCallingIdentity(); try { - superImpl.apply(code, new AttributionSource(shellUid, + superImpl.apply(clientId, code, new AttributionSource(shellUid, "com.android.shell", attributionSource.getAttributionTag(), attributionSource.getToken(), attributionSource.getNext()), skipProxyOperation); @@ -18359,7 +18478,7 @@ public void finishProxyOperation(int code, @NonNull AttributionSource attributio Binder.restoreCallingIdentity(identity); } } - superImpl.apply(code, attributionSource, skipProxyOperation); + superImpl.apply(clientId, code, attributionSource, skipProxyOperation); } private boolean isTargetOp(int code) { @@ -18517,4 +18636,37 @@ static void traceBegin(long traceTag, String methodName, String subInfo) { Trace.traceBegin(traceTag, methodName + subInfo); } } + + private class SwipeToScreenshotObserver extends ContentObserver { + + private final Context mContext; + + public SwipeToScreenshotObserver(Handler handler, Context context) { + super(handler); + mContext = context; + } + + public void registerObserver() { + mContext.getContentResolver().registerContentObserver( + Settings.System.getUriFor(Settings.System.THREE_FINGER_GESTURE), + false, this, UserHandle.USER_ALL); + update(); + } + + private void update() { + mIsSwipeToScreenshotEnabled = Settings.System.getIntForUser(mContext.getContentResolver(), + Settings.System.THREE_FINGER_GESTURE, 0, UserHandle.USER_CURRENT) == 1; + } + + public void onChange(boolean selfChange) { + update(); + } + } + + @Override + public boolean isSwipeToScreenshotGestureActive() { + synchronized (this) { + return mIsSwipeToScreenshotEnabled && SystemProperties.getBoolean("sys.android.screenshot", false); + } + } } diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java index 08c1de61d7fb..d450d17a60fb 100644 --- a/services/core/java/com/android/server/am/AppErrors.java +++ b/services/core/java/com/android/server/am/AppErrors.java @@ -1095,7 +1095,10 @@ void handleShowAnrUi(Message msg) { boolean showBackground = Settings.Secure.getIntForUser(mContext.getContentResolver(), Settings.Secure.ANR_SHOW_BACKGROUND, 0, mService.mUserController.getCurrentUserId()) != 0; - if (mService.mAtmInternal.canShowErrorDialogs() || showBackground) { + final boolean anrSilenced = mAppsNotReportingCrashes != null + && mAppsNotReportingCrashes.contains(proc.info.packageName); + if (!anrSilenced && + (mService.mAtmInternal.canShowErrorDialogs() || showBackground)) { AnrController anrController = errState.getDialogController().getAnrController(); if (anrController == null) { errState.getDialogController().showAnrDialogs(data); @@ -1120,7 +1123,7 @@ void handleShowAnrUi(Message msg) { MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_APP_ANR, AppNotRespondingDialog.CANT_SHOW); // Just kill the app if there is no dialog to be shown. - doKill = true; + doKill = !anrSilenced; } } if (doKill) { diff --git a/services/core/java/com/android/server/am/AppProfiler.java b/services/core/java/com/android/server/am/AppProfiler.java index e97654cde174..197c8e194ddf 100644 --- a/services/core/java/com/android/server/am/AppProfiler.java +++ b/services/core/java/com/android/server/am/AppProfiler.java @@ -944,6 +944,11 @@ void setAllowLowerMemLevelLocked(boolean allowLowerMemLevel) { mAllowLowerMemLevel = allowLowerMemLevel; } + @GuardedBy("mService") + boolean allowLowerMemLevelLocked() { + return mAllowLowerMemLevel; + } + @GuardedBy("mService") void setMemFactorOverrideLocked(@MemFactor int factor) { mMemFactorOverride = factor; diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index 606a09cb1cac..3db9fb52a47f 100644 --- a/services/core/java/com/android/server/am/BatteryStatsService.java +++ b/services/core/java/com/android/server/am/BatteryStatsService.java @@ -2980,4 +2980,17 @@ public void resetBattery(boolean forceUpdate) { public void suspendBatteryInput() { mBatteryManagerInternal.suspendBatteryInput(); } + + /** + * Battery stats and history reset + */ + @Override + public void resetStatistics() { + mContext.enforceCallingPermission( + android.Manifest.permission.RESET_BATTERY_STATS, null); + synchronized (mStats) { + mStats.resetAllStatsCmdLocked(); + mBatteryUsageStatsStore.removeAllSnapshots(); + } + } } diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java index 58569495f70d..f366cec96f9d 100644 --- a/services/core/java/com/android/server/am/BroadcastQueue.java +++ b/services/core/java/com/android/server/am/BroadcastQueue.java @@ -1921,7 +1921,7 @@ final void processNextBroadcastLocked(boolean fromMsg, boolean skipOomAdj) { info.activityInfo.applicationInfo, true, r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND, new HostingRecord(HostingRecord.HOSTING_TYPE_BROADCAST, r.curComponent, - r.intent.getAction()), + r.intent.getAction(), getHostingRecordTriggerType(r)), isActivityCapable ? ZYGOTE_POLICY_FLAG_LATENCY_SENSITIVE : ZYGOTE_POLICY_FLAG_EMPTY, (r.intent.getFlags() & Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0, false); if (r.curApp == null) { @@ -1944,6 +1944,16 @@ final void processNextBroadcastLocked(boolean fromMsg, boolean skipOomAdj) { mPendingBroadcastRecvIndex = recIdx; } + private String getHostingRecordTriggerType(BroadcastRecord r) { + if (r.alarm) { + return HostingRecord.TRIGGER_TYPE_ALARM; + } else if (r.pushMessage) { + return HostingRecord.TRIGGER_TYPE_PUSH_MESSAGE; + } else if (r.pushMessageOverQuota) { + return HostingRecord.TRIGGER_TYPE_PUSH_MESSAGE_OVER_QUOTA; + } + return HostingRecord.TRIGGER_TYPE_UNKNOWN; + } @Nullable private String getTargetPackage(BroadcastRecord r) { diff --git a/services/core/java/com/android/server/am/BroadcastRecord.java b/services/core/java/com/android/server/am/BroadcastRecord.java index ce4528bca887..baaae1d7a555 100644 --- a/services/core/java/com/android/server/am/BroadcastRecord.java +++ b/services/core/java/com/android/server/am/BroadcastRecord.java @@ -71,6 +71,8 @@ final class BroadcastRecord extends Binder { final boolean ordered; // serialize the send to receivers? final boolean sticky; // originated from existing sticky data? final boolean alarm; // originated from an alarm triggering? + final boolean pushMessage; // originated from a push message? + final boolean pushMessageOverQuota; // originated from a push message which was over quota? final boolean initialSticky; // initial broadcast from register to sticky? final int userId; // user id this broadcast was for final String resolvedType; // the resolved data type @@ -309,6 +311,8 @@ void dump(PrintWriter pw, String prefix, SimpleDateFormat sdf) { mBackgroundActivityStartsToken = backgroundActivityStartsToken; this.timeoutExempt = timeoutExempt; alarm = options != null && options.isAlarmBroadcast(); + pushMessage = options != null && options.isPushMessagingBroadcast(); + pushMessageOverQuota = options != null && options.isPushMessagingOverQuotaBroadcast(); } /** @@ -362,6 +366,8 @@ private BroadcastRecord(BroadcastRecord from, Intent newIntent) { mBackgroundActivityStartsToken = from.mBackgroundActivityStartsToken; timeoutExempt = from.timeoutExempt; alarm = from.alarm; + pushMessage = from.pushMessage; + pushMessageOverQuota = from.pushMessageOverQuota; } /** diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java index 992195611d0c..9582bfa6404e 100644 --- a/services/core/java/com/android/server/am/CachedAppOptimizer.java +++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java @@ -31,6 +31,7 @@ import android.os.PowerManagerInternal; import android.os.Process; import android.os.SystemClock; +import android.os.SystemProperties; import android.os.Trace; import android.provider.DeviceConfig; import android.provider.DeviceConfig.OnPropertiesChangedListener; @@ -40,6 +41,7 @@ import android.util.EventLog; import android.util.Pair; import android.util.Slog; +import android.util.BoostFramework; import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; @@ -63,6 +65,7 @@ public final class CachedAppOptimizer { // Flags stored in the DeviceConfig API. @VisibleForTesting static final String KEY_USE_COMPACTION = "use_compaction"; + @VisibleForTesting static final String KEY_COMPACTION_PRIORITY = "compaction_priority"; @VisibleForTesting static final String KEY_USE_FREEZER = "use_freezer"; @VisibleForTesting static final String KEY_COMPACT_ACTION_1 = "compact_action_1"; @VisibleForTesting static final String KEY_COMPACT_ACTION_2 = "compact_action_2"; @@ -205,7 +208,8 @@ interface ProcessDependencies { public void onPropertiesChanged(Properties properties) { synchronized (mPhenotypeFlagLock) { for (String name : properties.getKeyset()) { - if (KEY_USE_COMPACTION.equals(name)) { + if (KEY_USE_COMPACTION.equals(name) || + KEY_COMPACTION_PRIORITY.equals(name)) { updateUseCompaction(); } else if (KEY_COMPACT_ACTION_1.equals(name) || KEY_COMPACT_ACTION_2.equals(name)) { @@ -306,6 +310,7 @@ public void onChange(boolean selfChange, Uri uri) { private volatile boolean mUseFreezer = false; // set to DEFAULT in init() @GuardedBy("this") private int mFreezerDisableCount = 1; // Freezer is initially disabled, until enabled + public volatile int mCompactionPriority = Process.THREAD_GROUP_BACKGROUND; private final Random mRandom = new Random(); @GuardedBy("mPhenotypeFlagLock") @VisibleForTesting volatile float mCompactStatsdSampleRate = DEFAULT_STATSD_SAMPLE_RATE; @@ -362,6 +367,7 @@ protected boolean removeEldestEntry(Map.Entry eldest) { private final ProcessDependencies mProcessDependencies; private final ProcLocksReader mProcLocksReader; + public static BoostFramework mPerf = new BoostFramework(); public CachedAppOptimizer(ActivityManagerService am) { this(am, null, new DefaultProcessDependencies()); @@ -373,7 +379,7 @@ public CachedAppOptimizer(ActivityManagerService am) { mAm = am; mProcLock = am.mProcLock; mCachedAppOptimizerThread = new ServiceThread("CachedAppOptimizerThread", - Process.THREAD_GROUP_SYSTEM, true); + mCompactionPriority, true); mProcStateThrottle = new HashSet<>(); mProcessDependencies = processDependencies; mTestCallback = callback; @@ -408,6 +414,88 @@ public void init() { updateMinOomAdjThrottle(); updateMaxOomAdjThrottle(); } + setAppCompactProperties(); + } + + private void setAppCompactProperties() { + boolean useCompaction = + Boolean.valueOf(mPerf.perfGetProp("vendor.appcompact.enable_app_compact", + "false")); + int threadPriority = + Integer.valueOf(mPerf.perfGetProp("vendor.appcompact.thread_priority", + String.valueOf(Process.THREAD_GROUP_BACKGROUND))); + // Let the user change the group back to THREAD_GROUP_SYSYTEM + // For any other value, set it to THREAD_GROUP_BACKGROUND + if (threadPriority != Process.THREAD_GROUP_SYSTEM) + threadPriority = Process.THREAD_GROUP_BACKGROUND; + + int someCompactionType = + Integer.valueOf(mPerf.perfGetProp("vendor.appcompact.some_compact_type", + String.valueOf(COMPACT_ACTION_ANON_FLAG))); + int fullCompactionType = + Integer.valueOf(mPerf.perfGetProp("vendor.appcompact.full_compact_type", + String.valueOf(COMPACT_ACTION_ANON_FLAG))); + int compactThrottleSomeSome = + Integer.valueOf(mPerf.perfGetProp("vendor.appcompact.compact_throttle_somesome", + String.valueOf(DEFAULT_COMPACT_THROTTLE_1))); + int compactThrottleSomeFull = + Integer.valueOf(mPerf.perfGetProp("vendor.appcompact.compact_throttle_somefull", + String.valueOf(DEFAULT_COMPACT_THROTTLE_2))); + int compactThrottleFullSome = + Integer.valueOf(mPerf.perfGetProp("vendor.appcompact.compact_throttle_fullsome", + String.valueOf(DEFAULT_COMPACT_THROTTLE_3))); + int compactThrottleFullFull = + Integer.valueOf(mPerf.perfGetProp("vendor.appcompact.compact_throttle_fullfull", + String.valueOf(DEFAULT_COMPACT_THROTTLE_4))); + int compactThrottleBfgs = + Integer.valueOf(mPerf.perfGetProp("vendor.appcompact.compact_throttle_bfgs", + String.valueOf(DEFAULT_COMPACT_THROTTLE_5))); + int compactThrottlePersistent = + Integer.valueOf(mPerf.perfGetProp("vendor.appcompact.compact_throttle_persistent", + String.valueOf(DEFAULT_COMPACT_THROTTLE_6))); + int fullRssThrottleKB = + Integer.valueOf(mPerf.perfGetProp("vendor.appcompact.rss_throttle_kb", + String.valueOf(DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB))); + int deltaRssThrottleKB = + Integer.valueOf(mPerf.perfGetProp("vendor.appcompact.delta_rss_throttle_kb", + String.valueOf(DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB))); + + DeviceConfig.setProperty( + DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, KEY_COMPACT_ACTION_1, + String.valueOf(someCompactionType), true); + DeviceConfig.setProperty( + DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, KEY_COMPACT_ACTION_2, + String.valueOf(fullCompactionType), true); + DeviceConfig.setProperty( + DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, KEY_COMPACT_THROTTLE_1, + String.valueOf(compactThrottleSomeSome), true); + DeviceConfig.setProperty( + DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, KEY_COMPACT_THROTTLE_2, + String.valueOf(compactThrottleSomeFull), true); + DeviceConfig.setProperty( + DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, KEY_COMPACT_THROTTLE_3, + String.valueOf(compactThrottleFullSome), true); + DeviceConfig.setProperty( + DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, KEY_COMPACT_THROTTLE_4, + String.valueOf(compactThrottleFullFull), true); + DeviceConfig.setProperty( + DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, KEY_COMPACT_THROTTLE_5, + String.valueOf(compactThrottleBfgs), true); + DeviceConfig.setProperty( + DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, KEY_COMPACT_THROTTLE_6, + String.valueOf(compactThrottlePersistent), true); + DeviceConfig.setProperty( + DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, KEY_COMPACT_FULL_RSS_THROTTLE_KB, + String.valueOf(fullRssThrottleKB), true); + DeviceConfig.setProperty( + DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, KEY_COMPACT_FULL_DELTA_RSS_THROTTLE_KB, + String.valueOf(deltaRssThrottleKB), true); + DeviceConfig.setProperty( + DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, KEY_USE_COMPACTION, + String.valueOf(useCompaction), true); + DeviceConfig.setProperty( + DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, KEY_COMPACTION_PRIORITY, + String.valueOf(threadPriority), true); } /** @@ -433,6 +521,7 @@ void dump(PrintWriter pw) { pw.println("CachedAppOptimizer settings"); synchronized (mPhenotypeFlagLock) { pw.println(" " + KEY_USE_COMPACTION + "=" + mUseCompaction); + pw.println(" " + KEY_COMPACTION_PRIORITY + "=" + mCompactionPriority); pw.println(" " + KEY_COMPACT_ACTION_1 + "=" + mCompactActionSome); pw.println(" " + KEY_COMPACT_ACTION_2 + "=" + mCompactActionFull); pw.println(" " + KEY_COMPACT_THROTTLE_1 + "=" + mCompactThrottleSomeSome); @@ -659,19 +748,28 @@ void compactAllSystem() { */ @GuardedBy("mPhenotypeFlagLock") private void updateUseCompaction() { + // If this property is null there must have been some unexpected reset + String useCompaction = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, KEY_USE_COMPACTION); + if (useCompaction == null) { + setAppCompactProperties(); + } + mUseCompaction = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, KEY_USE_COMPACTION, DEFAULT_USE_COMPACTION); + mCompactionPriority = DeviceConfig.getInt(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + KEY_COMPACTION_PRIORITY, Process.THREAD_GROUP_BACKGROUND); + if (mUseCompaction && mCompactionHandler == null) { if (!mCachedAppOptimizerThread.isAlive()) { mCachedAppOptimizerThread.start(); } mCompactionHandler = new MemCompactionHandler(); - - Process.setThreadGroupAndCpuset(mCachedAppOptimizerThread.getThreadId(), - Process.THREAD_GROUP_SYSTEM); } + + Process.setThreadGroupAndCpuset(mCachedAppOptimizerThread.getThreadId(), + mCompactionPriority); } /** @@ -836,7 +934,8 @@ private void updateUseFreezer() { } Process.setThreadGroupAndCpuset(mCachedAppOptimizerThread.getThreadId(), - Process.THREAD_GROUP_SYSTEM); + mCompactionPriority); + } else { Slog.d(TAG_AM, "Freezer disabled"); enableFreezer(false); @@ -1236,7 +1335,8 @@ void onOomAdjustChanged(int oldAdj, int newAdj, ProcessRecord app) { && (newAdj == ProcessList.PREVIOUS_APP_ADJ || newAdj == ProcessList.HOME_APP_ADJ)) { // Perform a minor compaction when a perceptible app becomes the prev/home app compactAppSome(app, false); - } else if (oldAdj < ProcessList.CACHED_APP_MIN_ADJ + } else if ((oldAdj < ProcessList.CACHED_APP_MIN_ADJ + || oldAdj > ProcessList.CACHED_APP_MAX_ADJ) && newAdj >= ProcessList.CACHED_APP_MIN_ADJ && newAdj <= ProcessList.CACHED_APP_MAX_ADJ) { // Perform a major compaction when any app enters cached @@ -1304,7 +1404,7 @@ private boolean shouldOomAdjThrottleCompaction(ProcessRecord proc, int action) { // don't compact if the process has returned to perceptible // and this is only a cached/home/prev compaction - if ((action == COMPACT_ACTION_FILE || action == COMPACT_ACTION_FULL) + if ((action == COMPACT_PROCESS_SOME || action == COMPACT_PROCESS_FULL) && (proc.mState.getSetAdj() <= ProcessList.PERCEPTIBLE_APP_ADJ)) { if (DEBUG_COMPACTION) { Slog.d(TAG_AM, diff --git a/services/core/java/com/android/server/am/HostingRecord.java b/services/core/java/com/android/server/am/HostingRecord.java index f88a8ce83d02..30811a175bac 100644 --- a/services/core/java/com/android/server/am/HostingRecord.java +++ b/services/core/java/com/android/server/am/HostingRecord.java @@ -16,10 +16,30 @@ package com.android.server.am; +import static com.android.internal.util.FrameworkStatsLog.PROCESS_START_TIME__HOSTING_TYPE_ID__HOSTING_TYPE_ACTIVITY; +import static com.android.internal.util.FrameworkStatsLog.PROCESS_START_TIME__HOSTING_TYPE_ID__HOSTING_TYPE_ADDED_APPLICATION; +import static com.android.internal.util.FrameworkStatsLog.PROCESS_START_TIME__HOSTING_TYPE_ID__HOSTING_TYPE_BACKUP; +import static com.android.internal.util.FrameworkStatsLog.PROCESS_START_TIME__HOSTING_TYPE_ID__HOSTING_TYPE_BROADCAST; +import static com.android.internal.util.FrameworkStatsLog.PROCESS_START_TIME__HOSTING_TYPE_ID__HOSTING_TYPE_CONTENT_PROVIDER; +import static com.android.internal.util.FrameworkStatsLog.PROCESS_START_TIME__HOSTING_TYPE_ID__HOSTING_TYPE_EMPTY; +import static com.android.internal.util.FrameworkStatsLog.PROCESS_START_TIME__HOSTING_TYPE_ID__HOSTING_TYPE_LINK_FAIL; +import static com.android.internal.util.FrameworkStatsLog.PROCESS_START_TIME__HOSTING_TYPE_ID__HOSTING_TYPE_NEXT_ACTIVITY; +import static com.android.internal.util.FrameworkStatsLog.PROCESS_START_TIME__HOSTING_TYPE_ID__HOSTING_TYPE_NEXT_TOP_ACTIVITY; +import static com.android.internal.util.FrameworkStatsLog.PROCESS_START_TIME__HOSTING_TYPE_ID__HOSTING_TYPE_ON_HOLD; +import static com.android.internal.util.FrameworkStatsLog.PROCESS_START_TIME__HOSTING_TYPE_ID__HOSTING_TYPE_RESTART; +import static com.android.internal.util.FrameworkStatsLog.PROCESS_START_TIME__HOSTING_TYPE_ID__HOSTING_TYPE_SERVICE; +import static com.android.internal.util.FrameworkStatsLog.PROCESS_START_TIME__HOSTING_TYPE_ID__HOSTING_TYPE_SYSTEM; +import static com.android.internal.util.FrameworkStatsLog.PROCESS_START_TIME__HOSTING_TYPE_ID__HOSTING_TYPE_TOP_ACTIVITY; +import static com.android.internal.util.FrameworkStatsLog.PROCESS_START_TIME__TRIGGER_TYPE__TRIGGER_TYPE_ALARM; +import static com.android.internal.util.FrameworkStatsLog.PROCESS_START_TIME__TRIGGER_TYPE__TRIGGER_TYPE_JOB; +import static com.android.internal.util.FrameworkStatsLog.PROCESS_START_TIME__TRIGGER_TYPE__TRIGGER_TYPE_PUSH_MESSAGE; +import static com.android.internal.util.FrameworkStatsLog.PROCESS_START_TIME__TRIGGER_TYPE__TRIGGER_TYPE_PUSH_MESSAGE_OVER_QUOTA; +import static com.android.internal.util.FrameworkStatsLog.PROCESS_START_TIME__TRIGGER_TYPE__TRIGGER_TYPE_UNKNOWN; +import static com.android.internal.util.FrameworkStatsLog.PROCESS_START_TIME__TYPE__UNKNOWN; + import android.annotation.NonNull; import android.annotation.Nullable; import android.content.ComponentName; -import android.os.ProcessStartTime; /** * This class describes various information required to start a process. @@ -32,6 +52,9 @@ * * The {@code mHostingZygote} field describes from which Zygote the new process should be spawned. * + * The {@code mTriggerType} field describes the trigger that started this processs. This could be + * an alarm or a push-message for a broadcast, for example. This is purely for logging and stats. + * * {@code mDefiningPackageName} contains the packageName of the package that defines the * component we want to start; this can be different from the packageName and uid in the * ApplicationInfo that we're creating the process with, in case the service is a @@ -71,7 +94,13 @@ public final class HostingRecord { public static final String HOSTING_TYPE_TOP_ACTIVITY = "top-activity"; public static final String HOSTING_TYPE_EMPTY = ""; - private @NonNull final String mHostingType; + public static final String TRIGGER_TYPE_UNKNOWN = "unknown"; + public static final String TRIGGER_TYPE_ALARM = "alarm"; + public static final String TRIGGER_TYPE_PUSH_MESSAGE = "push_message"; + public static final String TRIGGER_TYPE_PUSH_MESSAGE_OVER_QUOTA = "push_message_over_quota"; + public static final String TRIGGER_TYPE_JOB = "job"; + + @NonNull private final String mHostingType; private final String mHostingName; private final int mHostingZygote; private final String mDefiningPackageName; @@ -79,11 +108,12 @@ public final class HostingRecord { private final boolean mIsTopApp; private final String mDefiningProcessName; @Nullable private final String mAction; + @NonNull private final String mTriggerType; public HostingRecord(@NonNull String hostingType) { this(hostingType, null /* hostingName */, REGULAR_ZYGOTE, null /* definingPackageName */, -1 /* mDefiningUid */, false /* isTopApp */, null /* definingProcessName */, - null /* action */); + null /* action */, TRIGGER_TYPE_UNKNOWN); } public HostingRecord(@NonNull String hostingType, ComponentName hostingName) { @@ -91,22 +121,24 @@ public HostingRecord(@NonNull String hostingType, ComponentName hostingName) { } public HostingRecord(@NonNull String hostingType, ComponentName hostingName, - @Nullable String action) { + @Nullable String action, @Nullable String triggerType) { this(hostingType, hostingName.toShortString(), REGULAR_ZYGOTE, null /* definingPackageName */, -1 /* mDefiningUid */, false /* isTopApp */, - null /* definingProcessName */, action); + null /* definingProcessName */, action, triggerType); } public HostingRecord(@NonNull String hostingType, ComponentName hostingName, - String definingPackageName, int definingUid, String definingProcessName) { + String definingPackageName, int definingUid, String definingProcessName, + String triggerType) { this(hostingType, hostingName.toShortString(), REGULAR_ZYGOTE, definingPackageName, - definingUid, false /* isTopApp */, definingProcessName, null /* action */); + definingUid, false /* isTopApp */, definingProcessName, null /* action */, + triggerType); } public HostingRecord(@NonNull String hostingType, ComponentName hostingName, boolean isTopApp) { this(hostingType, hostingName.toShortString(), REGULAR_ZYGOTE, null /* definingPackageName */, -1 /* mDefiningUid */, isTopApp /* isTopApp */, - null /* definingProcessName */, null /* action */); + null /* definingProcessName */, null /* action */, TRIGGER_TYPE_UNKNOWN); } public HostingRecord(@NonNull String hostingType, String hostingName) { @@ -121,12 +153,12 @@ private HostingRecord(@NonNull String hostingType, ComponentName hostingName, private HostingRecord(@NonNull String hostingType, String hostingName, int hostingZygote) { this(hostingType, hostingName, hostingZygote, null /* definingPackageName */, -1 /* mDefiningUid */, false /* isTopApp */, null /* definingProcessName */, - null /* action */); + null /* action */, TRIGGER_TYPE_UNKNOWN); } private HostingRecord(@NonNull String hostingType, String hostingName, int hostingZygote, String definingPackageName, int definingUid, boolean isTopApp, - String definingProcessName, @Nullable String action) { + String definingProcessName, @Nullable String action, String triggerType) { mHostingType = hostingType; mHostingName = hostingName; mHostingZygote = hostingZygote; @@ -135,6 +167,7 @@ private HostingRecord(@NonNull String hostingType, String hostingName, int hosti mIsTopApp = isTopApp; mDefiningProcessName = definingProcessName; mAction = action; + mTriggerType = triggerType; } public @NonNull String getType() { @@ -188,6 +221,11 @@ public String getDefiningProcessName() { return mAction; } + /** Returns the type of trigger that led to this process start. */ + public @NonNull String getTriggerType() { + return mTriggerType; + } + /** * Creates a HostingRecord for a process that must spawn from the webview zygote * @param hostingName name of the component to be hosted in this process @@ -197,7 +235,7 @@ public static HostingRecord byWebviewZygote(ComponentName hostingName, String definingPackageName, int definingUid, String definingProcessName) { return new HostingRecord(HostingRecord.HOSTING_TYPE_EMPTY, hostingName.toShortString(), WEBVIEW_ZYGOTE, definingPackageName, definingUid, false /* isTopApp */, - definingProcessName, null /* action */); + definingProcessName, null /* action */, TRIGGER_TYPE_UNKNOWN); } /** @@ -211,7 +249,7 @@ public static HostingRecord byAppZygote(ComponentName hostingName, String defini int definingUid, String definingProcessName) { return new HostingRecord(HostingRecord.HOSTING_TYPE_EMPTY, hostingName.toShortString(), APP_ZYGOTE, definingPackageName, definingUid, false /* isTopApp */, - definingProcessName, null /* action */); + definingProcessName, null /* action */, TRIGGER_TYPE_UNKNOWN); } /** @@ -236,35 +274,55 @@ public boolean usesWebviewZygote() { public static int getHostingTypeIdStatsd(@NonNull String hostingType) { switch(hostingType) { case HOSTING_TYPE_ACTIVITY: - return ProcessStartTime.HOSTING_TYPE_ACTIVITY; + return PROCESS_START_TIME__HOSTING_TYPE_ID__HOSTING_TYPE_ACTIVITY; case HOSTING_TYPE_ADDED_APPLICATION: - return ProcessStartTime.HOSTING_TYPE_ADDED_APPLICATION; + return PROCESS_START_TIME__HOSTING_TYPE_ID__HOSTING_TYPE_ADDED_APPLICATION; case HOSTING_TYPE_BACKUP: - return ProcessStartTime.HOSTING_TYPE_BACKUP; + return PROCESS_START_TIME__HOSTING_TYPE_ID__HOSTING_TYPE_BACKUP; case HOSTING_TYPE_BROADCAST: - return ProcessStartTime.HOSTING_TYPE_BROADCAST; + return PROCESS_START_TIME__HOSTING_TYPE_ID__HOSTING_TYPE_BROADCAST; case HOSTING_TYPE_CONTENT_PROVIDER: - return ProcessStartTime.HOSTING_TYPE_CONTENT_PROVIDER; + return PROCESS_START_TIME__HOSTING_TYPE_ID__HOSTING_TYPE_CONTENT_PROVIDER; case HOSTING_TYPE_LINK_FAIL: - return ProcessStartTime.HOSTING_TYPE_LINK_FAIL; + return PROCESS_START_TIME__HOSTING_TYPE_ID__HOSTING_TYPE_LINK_FAIL; case HOSTING_TYPE_ON_HOLD: - return ProcessStartTime.HOSTING_TYPE_ON_HOLD; + return PROCESS_START_TIME__HOSTING_TYPE_ID__HOSTING_TYPE_ON_HOLD; case HOSTING_TYPE_NEXT_ACTIVITY: - return ProcessStartTime.HOSTING_TYPE_NEXT_ACTIVITY; + return PROCESS_START_TIME__HOSTING_TYPE_ID__HOSTING_TYPE_NEXT_ACTIVITY; case HOSTING_TYPE_NEXT_TOP_ACTIVITY: - return ProcessStartTime.HOSTING_TYPE_NEXT_TOP_ACTIVITY; + return PROCESS_START_TIME__HOSTING_TYPE_ID__HOSTING_TYPE_NEXT_TOP_ACTIVITY; case HOSTING_TYPE_RESTART: - return ProcessStartTime.HOSTING_TYPE_RESTART; + return PROCESS_START_TIME__HOSTING_TYPE_ID__HOSTING_TYPE_RESTART; case HOSTING_TYPE_SERVICE: - return ProcessStartTime.HOSTING_TYPE_SERVICE; + return PROCESS_START_TIME__HOSTING_TYPE_ID__HOSTING_TYPE_SERVICE; case HOSTING_TYPE_SYSTEM: - return ProcessStartTime.HOSTING_TYPE_SYSTEM; + return PROCESS_START_TIME__HOSTING_TYPE_ID__HOSTING_TYPE_SYSTEM; case HOSTING_TYPE_TOP_ACTIVITY: - return ProcessStartTime.HOSTING_TYPE_TOP_ACTIVITY; + return PROCESS_START_TIME__HOSTING_TYPE_ID__HOSTING_TYPE_TOP_ACTIVITY; case HOSTING_TYPE_EMPTY: - return ProcessStartTime.HOSTING_TYPE_EMPTY; + return PROCESS_START_TIME__HOSTING_TYPE_ID__HOSTING_TYPE_EMPTY; + default: + return PROCESS_START_TIME__TYPE__UNKNOWN; + } + } + + /** + * Map the string triggerType to enum TriggerType defined in ProcessStartTime proto. + * @param triggerType + * @return enum TriggerType defined in ProcessStartTime proto + */ + public static int getTriggerTypeForStatsd(@NonNull String triggerType) { + switch(triggerType) { + case TRIGGER_TYPE_ALARM: + return PROCESS_START_TIME__TRIGGER_TYPE__TRIGGER_TYPE_ALARM; + case TRIGGER_TYPE_PUSH_MESSAGE: + return PROCESS_START_TIME__TRIGGER_TYPE__TRIGGER_TYPE_PUSH_MESSAGE; + case TRIGGER_TYPE_PUSH_MESSAGE_OVER_QUOTA: + return PROCESS_START_TIME__TRIGGER_TYPE__TRIGGER_TYPE_PUSH_MESSAGE_OVER_QUOTA; + case TRIGGER_TYPE_JOB: + return PROCESS_START_TIME__TRIGGER_TYPE__TRIGGER_TYPE_JOB; default: - return ProcessStartTime.HOSTING_TYPE_UNKNOWN; + return PROCESS_START_TIME__TRIGGER_TYPE__TRIGGER_TYPE_UNKNOWN; } } } diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java index bc939d6279cd..e80acd977494 100644 --- a/services/core/java/com/android/server/am/OomAdjuster.java +++ b/services/core/java/com/android/server/am/OomAdjuster.java @@ -51,6 +51,7 @@ import static android.os.Process.THREAD_PRIORITY_DISPLAY; import static android.os.Process.THREAD_PRIORITY_TOP_APP_BOOST; import static android.os.Process.setProcessGroup; +import static android.os.Process.setCgroupProcsProcessGroup; import static android.os.Process.setThreadPriority; import static android.os.Process.setThreadScheduler; @@ -97,9 +98,11 @@ import android.os.Process; import android.os.RemoteException; import android.os.SystemClock; +import android.os.SystemProperties; import android.os.Trace; import android.util.ArrayMap; import android.util.ArraySet; +import android.util.BoostFramework; import android.util.Slog; import android.util.proto.ProtoOutputStream; @@ -230,6 +233,26 @@ public class OomAdjuster { private final ProcessList mProcessList; private final ActivityManagerGlobalLock mProcLock; + // Min aging threshold in milliseconds to consider a B-service + int mMinBServiceAgingTime = 5000; + // Threshold for B-services when in memory pressure + int mBServiceAppThreshold = 5; + // Enable B-service aging propagation on memory pressure. + boolean mEnableBServicePropagation = false; + // Process in same process Group keep in same cgroup + boolean mEnableProcessGroupCgroupFollow = false; + boolean mProcessGroupCgroupFollowDex2oatOnly = false; + // Enable hooks for background apps transition + boolean mEnableBgt = false; + + public static BoostFramework mPerf = new BoostFramework(); + + //Per Task Boost of top-app renderThread + public static BoostFramework mPerfBoost = new BoostFramework(); + public static int mPerfHandle = -1; + public static int mCurRenderThreadTid = -1; + public static boolean mIsTopAppRenderThreadBoostEnabled = false; + private final int mNumSlots; private final ArrayList mTmpProcessList = new ArrayList(); private final ArrayList mTmpBecameIdle = new ArrayList(); @@ -287,19 +310,34 @@ private static ServiceThread createAdjusterThread() { mCachedAppOptimizer = new CachedAppOptimizer(mService); mCacheOomRanker = new CacheOomRanker(service); + if(mPerf != null) { + mMinBServiceAgingTime = Integer.valueOf(mPerf.perfGetProp("ro.vendor.qti.sys.fw.bservice_age", "5000")); + mBServiceAppThreshold = Integer.valueOf(mPerf.perfGetProp("ro.vendor.qti.sys.fw.bservice_limit", "5")); + mEnableBServicePropagation = Boolean.parseBoolean(mPerf.perfGetProp("ro.vendor.qti.sys.fw.bservice_enable", "false")); + mEnableProcessGroupCgroupFollow = Boolean.parseBoolean(mPerf.perfGetProp("ro.vendor.qti.cgroup_follow.enable", "false")); + mProcessGroupCgroupFollowDex2oatOnly = Boolean.parseBoolean(mPerf.perfGetProp("ro.vendor.qti.cgroup_follow.dex2oat_only", "false")); + mIsTopAppRenderThreadBoostEnabled = Boolean.parseBoolean(mPerf.perfGetProp("vendor.perf.topAppRenderThreadBoost.enable", "false")); + mEnableBgt = Boolean.parseBoolean(mPerf.perfGetProp("vendor.perf.bgt.enable","false")); + } + mProcessGroupHandler = new Handler(adjusterThread.getLooper(), msg -> { final int pid = msg.arg1; final int group = msg.arg2; + final ProcessRecord app = (ProcessRecord)msg.obj; if (pid == ActivityManagerService.MY_PID) { // Skip setting the process group for system_server, keep it as default. return true; } if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) { Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "setProcessGroup " - + msg.obj + " to " + group); + + app.processName + " to " + group); } try { - setProcessGroup(pid, group); + if (mEnableProcessGroupCgroupFollow) { + setCgroupProcsProcessGroup(app.info.uid, pid, group, mProcessGroupCgroupFollowDex2oatOnly); + } else { + setProcessGroup(pid, group); + } } catch (Exception e) { if (DEBUG_ALL) { Slog.w(TAG, "Failed setting process group of " + pid + " to " + group, e); @@ -1057,9 +1095,42 @@ private boolean updateAndTrimProcessLSP(final long now, final long nowElapsed, int numCachedExtraGroup = 0; int numEmpty = 0; int numTrimming = 0; + ProcessRecord selectedAppRecord = null; + long serviceLastActivity = 0; + int numBServices = 0; + + for (int i = numLru - 1; i >= 0; i--) { ProcessRecord app = lruList.get(i); + if (mEnableBServicePropagation && app.mState.isServiceB() + && (app.mState.getCurAdj() == ProcessList.SERVICE_B_ADJ)) { + numBServices++; + for (int s = app.mServices.numberOfRunningServices() - 1; s >= 0; s--) { + ServiceRecord sr = app.mServices.getRunningServiceAt(s); + if (DEBUG_OOM_ADJ) Slog.d(TAG,"app.processName = " + app.processName + + " serviceb = " + app.mState.isServiceB() + " s = " + s + " sr.lastActivity = " + + sr.lastActivity + " packageName = " + sr.packageName + + " processName = " + sr.processName); + if (SystemClock.uptimeMillis() - sr.lastActivity + < mMinBServiceAgingTime) { + if (DEBUG_OOM_ADJ) { + Slog.d(TAG,"Not aged enough!!!"); + } + continue; + } + if (serviceLastActivity == 0) { + serviceLastActivity = sr.lastActivity; + selectedAppRecord = app; + } else if (sr.lastActivity < serviceLastActivity) { + serviceLastActivity = sr.lastActivity; + selectedAppRecord = app; + } + } + } + if (DEBUG_OOM_ADJ && selectedAppRecord != null) Slog.d(TAG, + "Identified app.processName = " + selectedAppRecord.processName + + " app.pid = " + selectedAppRecord.getPid()); final ProcessStateRecord state = app.mState; if (!app.isKilledByAm() && app.getThread() != null) { // We don't need to apply the update for the process which didn't get computed @@ -1144,6 +1215,14 @@ private boolean updateAndTrimProcessLSP(final long now, final long nowElapsed, } } } + if ((numBServices > mBServiceAppThreshold) && (true == mService.mAppProfiler.allowLowerMemLevelLocked()) + && (selectedAppRecord != null)) { + ProcessList.setOomAdj(selectedAppRecord.getPid(), selectedAppRecord.info.uid, + ProcessList.CACHED_APP_MAX_ADJ); + selectedAppRecord.mState.setSetAdj(selectedAppRecord.mState.getCurAdj()); + if (DEBUG_OOM_ADJ) Slog.d(TAG,"app.processName = " + selectedAppRecord.processName + + " app.pid = " + selectedAppRecord.getPid() + " is moved to higher adj"); + } return mService.mAppProfiler.updateLowMemStateLSP(numCached, numEmpty, numTrimming); } @@ -1581,7 +1660,26 @@ private boolean computeOomAdjLSP(ProcessRecord app, int cachedAdj, schedGroup = ProcessList.SCHED_GROUP_TOP_APP; state.setAdjType("running-remote-anim"); procState = PROCESS_STATE_CUR_TOP; - if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { + + if(mIsTopAppRenderThreadBoostEnabled) { + if(mCurRenderThreadTid != app.getRenderThreadTid() && app.getRenderThreadTid() > 0) { + mCurRenderThreadTid = app.getRenderThreadTid(); + if (mPerfBoost != null) { + Slog.d(TAG, "TOP-APP: pid:" + app.getPid() + ", processName: " + + app.processName + ", renderThreadTid: " + app.getRenderThreadTid()); + if (mPerfHandle >= 0) { + mPerfBoost.perfLockRelease(); + mPerfHandle = -1; + } + mPerfHandle = mPerfBoost.perfHint(BoostFramework.VENDOR_HINT_BOOST_RENDERTHREAD, + app.processName, app.getRenderThreadTid(), 1); + Slog.d(TAG, "VENDOR_HINT_BOOST_RENDERTHREAD perfHint was called. mPerfHandle: " + + mPerfHandle); + } + } + } + + if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making running remote anim: " + app); } } else if (app.getActiveInstrumentation() != null) { @@ -2593,7 +2691,32 @@ private boolean applyOomAdjLSP(ProcessRecord app, boolean doingAll, long now, } if (state.getCurAdj() != state.getSetAdj()) { - ProcessList.setOomAdj(app.getPid(), app.uid, state.getCurAdj()); + // Hooks for background apps transition + if (mEnableBgt) { + if ((state.getSetAdj() >= ProcessList.CACHED_APP_MIN_ADJ && + state.getSetAdj() <= ProcessList.CACHED_APP_MAX_ADJ) && + state.getCurAdj() == ProcessList.FOREGROUND_APP_ADJ && + state.hasForegroundActivities()) { + Slog.d(TAG,"App adj change from cached state to fg state : " + + app.getPid() + " " + app.processName); + if (mPerf != null) { + int fgAppPerfLockArgs[] = {BoostFramework.MPCTLV3_GPU_IS_APP_FG, app.getPid()}; + mPerf.perfLockAcquire(10, fgAppPerfLockArgs); + } + } + if(state.getSetAdj() == ProcessList.PREVIOUS_APP_ADJ && + (state.getCurAdj() >= ProcessList.CACHED_APP_MIN_ADJ && + state.getCurAdj() <= ProcessList.CACHED_APP_MAX_ADJ) && + app.hasActivities()) { + Slog.d(TAG,"App adj change from previous state to cached state : " + + app.getPid() + " " + app.processName); + if (mPerf != null) { + int bgAppPerfLockArgs[] = {BoostFramework.MPCTLV3_GPU_IS_APP_BG, app.getPid()}; + mPerf.perfLockAcquire(10, bgAppPerfLockArgs); + } + } + } + ProcessList.setOomAdj(app.getPid(), app.uid, app.mState.getCurAdj()); if (DEBUG_SWITCH || DEBUG_OOM_ADJ || mService.mCurOomAdjUid == app.info.uid) { String msg = "Set " + app.getPid() + " " + app.processName + " adj " + state.getCurAdj() + ": " + state.getAdjType(); @@ -2638,7 +2761,7 @@ private boolean applyOomAdjLSP(ProcessRecord app, boolean doingAll, long now, break; } mProcessGroupHandler.sendMessage(mProcessGroupHandler.obtainMessage( - 0 /* unused */, app.getPid(), processGroup, app.processName)); + 0 /* unused */, app.getPid(), processGroup, app)); try { final int renderThreadTid = app.getRenderThreadTid(); if (curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP) { diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java index bda60ff2172b..8624ee031a93 100644 --- a/services/core/java/com/android/server/am/PendingIntentRecord.java +++ b/services/core/java/com/android/server/am/PendingIntentRecord.java @@ -379,11 +379,16 @@ public int sendInner(int code, Intent intent, String resolvedType, IBinder allow resolvedType = key.requestResolvedType; } - // Apply any launch flags from the ActivityOptions. This is to ensure that the caller - // can specify a consistent launch mode even if the PendingIntent is immutable + // Apply any launch flags from the ActivityOptions. This is used only by SystemUI + // to ensure that we can launch the pending intent with a consistent launch mode even + // if the provided PendingIntent is immutable (ie. to force an activity to launch into + // a new task, or to launch multiple instances if supported by the app) final ActivityOptions opts = ActivityOptions.fromBundle(options); if (opts != null) { - finalIntent.addFlags(opts.getPendingIntentLaunchFlags()); + // TODO(b/254490217): Move this check into SafeActivityOptions + if (controller.mAtmInternal.isCallerRecents(Binder.getCallingUid())) { + finalIntent.addFlags(opts.getPendingIntentLaunchFlags()); + } } // Extract options before clearing calling identity diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index 3eac4066dc86..721ae417828c 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -107,6 +107,7 @@ import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; +import android.util.BoostFramework; import android.util.DebugUtils; import android.util.EventLog; import android.util.LongSparseArray; @@ -142,6 +143,8 @@ import dalvik.system.VMRuntime; +import ink.kaleidoscope.server.ParallelSpaceManagerService; + import java.io.DataInputStream; import java.io.File; import java.io.FileDescriptor; @@ -526,6 +529,11 @@ public final class ProcessList { ActivityManagerGlobalLock mProcLock; + /** + * BoostFramework Object + */ + public static BoostFramework mPerfServiceStartHint = new BoostFramework(); + final class IsolatedUidRange { @VisibleForTesting public final int mFirstUid; @@ -1616,6 +1624,22 @@ private int[] computeGidsForProcess(int mountExternal, int uid, int[] permGids, // EmulatedVolumes: /data/media and /mnt/expand//data/media // PublicVolumes: /mnt/media_rw/ gidList.add(Process.MEDIA_RW_GID); + + // HACK: For legacy devices running fuse above sdcardfs. We have to grant it + // gids of other users to make the cross-user file management possible. + // This is dirty. But I can't come up with a better idea which doesn't need + // to hack the kernel. + for (int userId : ParallelSpaceManagerService.getCurrentParallelUserIds()) { + gidList.add(UserHandle.getUserGid(userId)); + // Not needed in theory. Add it just in case. This could happened when + // dropping sdcardfs without wiping data and things get broken. + gidList.add(UserHandle.getUid(userId, Process.MEDIA_RW_GID)); + } + // Make sure parallel spaces are ready to visit the owner too. + if (ParallelSpaceManagerService.isCurrentParallelUser(UserHandle.getUserId(uid))) { + int ownerId = ParallelSpaceManagerService.getCurrentParallelOwnerId(); + gidList.add(UserHandle.getUserGid(ownerId)); + } } if (externalStorageAccess) { // Apps with MANAGE_EXTERNAL_STORAGE PERMISSION need the external_storage gid to access @@ -2330,6 +2354,16 @@ private Process.ProcessStartResult startProcess(HostingRecord hostingRecord, Str storageManagerInternal.prepareStorageDirs(userId, pkgDataInfoMap.keySet(), app.processName); } + if (mPerfServiceStartHint != null) { + if ((hostingRecord.getType() != null) + && (hostingRecord.getType().equals(HostingRecord.HOSTING_TYPE_NEXT_ACTIVITY) + || hostingRecord.getType().equals(HostingRecord.HOSTING_TYPE_NEXT_TOP_ACTIVITY))) { + //TODO: not acting on pre-activity + if (startResult != null) { + mPerfServiceStartHint.perfHint(BoostFramework.VENDOR_HINT_FIRST_LAUNCH_BOOST, app.processName, startResult.pid, BoostFramework.Launch.TYPE_START_PROC); + } + } + } checkSlow(startTime, "startProcess: returned from zygote!"); return startResult; } finally { diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java index 07b6fcdcb0ca..1a6311bcb4e9 100644 --- a/services/core/java/com/android/server/am/ProcessRecord.java +++ b/services/core/java/com/android/server/am/ProcessRecord.java @@ -47,6 +47,7 @@ import android.util.Slog; import android.util.TimeUtils; import android.util.proto.ProtoOutputStream; +import android.util.BoostFramework; import com.android.internal.annotations.CompositeRWLock; import com.android.internal.annotations.GuardedBy; @@ -606,6 +607,21 @@ IApplicationThread getThread() { @GuardedBy({"mService", "mProcLock"}) public void makeActive(IApplicationThread thread, ProcessStatsService tracker) { + // TODO(b/180501180): Add back this logging message. + /* + String seempStr = "app_uid=" + uid + + ",app_pid=" + pid + ",oom_adj=" + curAdj + + ",setAdj=" + setAdj + ",hasShownUi=" + (hasShownUi ? 1 : 0) + + ",cached=" + (mCached ? 1 : 0) + + ",fA=" + (mHasForegroundActivities ? 1 : 0) + + ",fS=" + (mHasForegroundServices ? 1 : 0) + + ",systemNoUi=" + (systemNoUi ? 1 : 0) + + ",curSchedGroup=" + mCurSchedGroup + + ",curProcState=" + getCurProcState() + ",setProcState=" + setProcState + + ",killed=" + (killed ? 1 : 0) + ",killedByAm=" + (killedByAm ? 1 : 0) + + ",isDebugging=" + (isDebugging() ? 1 : 0); + android.util.SeempLog.record_str(386, seempStr); + */ mProfile.onProcessActive(thread, tracker); mThread = thread; mWindowProcessController.setThread(thread); @@ -613,6 +629,21 @@ public void makeActive(IApplicationThread thread, ProcessStatsService tracker) { @GuardedBy({"mService", "mProcLock"}) public void makeInactive(ProcessStatsService tracker) { + // TODO(b/180501180): Add back this logging message. + /* + String seempStr = "app_uid=" + uid + + ",app_pid=" + pid + ",oom_adj=" + curAdj + + ",setAdj=" + setAdj + ",hasShownUi=" + (hasShownUi ? 1 : 0) + + ",cached=" + (mCached ? 1 : 0) + + ",fA=" + (mHasForegroundActivities ? 1 : 0) + + ",fS=" + (mHasForegroundServices ? 1 : 0) + + ",systemNoUi=" + (systemNoUi ? 1 : 0) + + ",curSchedGroup=" + mCurSchedGroup + + ",curProcState=" + getCurProcState() + ",setProcState=" + setProcState + + ",killed=" + (killed ? 1 : 0) + ",killedByAm=" + (killedByAm ? 1 : 0) + + ",isDebugging=" + (isDebugging() ? 1 : 0); + android.util.SeempLog.record_str(387, seempStr); + */ mThread = null; mWindowProcessController.setThread(null); mProfile.onProcessInactive(tracker); @@ -1075,6 +1106,7 @@ void killLocked(String reason, String description, @Reason int reasonCode, && mErrorState.getAnrAnnotation() != null) { description = description + ": " + mErrorState.getAnrAnnotation(); } + BoostFramework ux_perf = new BoostFramework(); if (mService != null && (noisy || info.uid == mService.mCurOomAdjUid)) { mService.reportUidInfoMessageLocked(TAG, "Killing " + toShortString() + " (adj " + mState.getSetAdj() @@ -1096,6 +1128,16 @@ void killLocked(String reason, String description, @Reason int reasonCode, mKillTime = SystemClock.uptimeMillis(); } } + if (ux_perf != null && !mService.mForceStopKill && !mErrorState.isNotResponding() + && !mErrorState.isCrashing()) { + if (ux_perf.board_first_api_lvl < BoostFramework.VENDOR_T_API_LEVEL && + ux_perf.board_api_lvl < BoostFramework.VENDOR_T_API_LEVEL) { + ux_perf.perfUXEngine_events(BoostFramework.UXE_EVENT_KILL, 0, this.processName, 0); + } + ux_perf.perfEvent(BoostFramework.VENDOR_HINT_KILL,this.processName, 2, 0,getPid()); + } else { + mService.mForceStopKill = false; + } Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); } } diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java index 44b186e1541f..7b2315ed02bd 100644 --- a/services/core/java/com/android/server/am/UserController.java +++ b/services/core/java/com/android/server/am/UserController.java @@ -67,10 +67,10 @@ import android.content.PermissionChecker; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; -import android.content.pm.PackagePartitions; import android.content.pm.UserInfo; import android.os.BatteryStats; import android.os.Binder; +import android.os.Build; import android.os.Bundle; import android.os.Debug; import android.os.Handler; @@ -761,12 +761,12 @@ private void finishUserUnlocked(final UserState uss) { // purposefully block sending BOOT_COMPLETED until after all // PRE_BOOT receivers are finished to avoid ANR'ing apps final UserInfo info = getUserInfo(userId); - if (!Objects.equals(info.lastLoggedInFingerprint, PackagePartitions.FINGERPRINT) + if (!Objects.equals(info.lastLoggedInFingerprint, String.valueOf(Build.TIME)) || SystemProperties.getBoolean("persist.pm.mock-upgrade", false)) { - // Suppress double notifications for managed profiles that - // were unlocked automatically as part of their parent user being + // Suppress double notifications for managed profiles and parallel space + // that were unlocked automatically as part of their parent user being // unlocked. TODO(b/217442918): this code doesn't work correctly. - final boolean quiet = info.isManagedProfile(); + final boolean quiet = info.isManagedProfile() || info.isParallel(); mInjector.sendPreBootBroadcast(userId, quiet, () -> finishUserUnlockedCompleted(uss)); } else { @@ -1517,7 +1517,7 @@ private boolean startUserInternal(@UserIdInt int userId, boolean foreground, Slogf.w(TAG, "No user info for user #" + userId); return false; } - if (foreground && userInfo.isProfile()) { + if (foreground && (userInfo.isProfile() || userInfo.isParallel())) { Slogf.w(TAG, "Cannot switch to User #" + userId + ": not a full user"); return false; } diff --git a/services/core/java/com/android/server/app/AppLockConfig.kt b/services/core/java/com/android/server/app/AppLockConfig.kt new file mode 100644 index 000000000000..59e56d8dfb9e --- /dev/null +++ b/services/core/java/com/android/server/app/AppLockConfig.kt @@ -0,0 +1,251 @@ +/* + * 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 com.android.server.app + +import android.app.AppLockData +import android.app.AppLockManager.DEFAULT_BIOMETRICS_ALLOWED +import android.app.AppLockManager.DEFAULT_TIMEOUT +import android.os.FileUtils +import android.os.FileUtils.S_IRWXU +import android.os.FileUtils.S_IRWXG +import android.util.ArrayMap +import android.util.Slog + +import java.io.File +import java.io.IOException + +import org.json.JSONArray +import org.json.JSONException +import org.json.JSONObject + +private const val APP_LOCK_DIR_NAME = "app_lock" +private const val APP_LOCK_CONFIG_FILE = "app_lock_config.json" + +private const val CURRENT_VERSION = 1 + +// Only in version 0 +private const val KEY_PACKAGES = "packages" +private const val KEY_SECURE_NOTIFICATION = "secure_notification" + +// From version 1 and up. Non existent version key +// is considered as version 0 +private const val KEY_VERSION = "version" +private const val KEY_TIMEOUT = "timeout" +private const val KEY_APP_LOCK_DATA_LIST = "app_lock_data_list" +private const val KEY_PACKAGE_NAME = "package_name" +private const val KEY_REDACT_NOTIFICATION = "redact_notification" +private const val KEY_BIOMETRICS_ALLOWED = "biometrics_allowed" + +/** + * Container for app lock configuration. Also handles logic of reading + * and writing configuration to disk, serialized as a JSON file. + * All operations must be synchronized with an external lock. + * + * @hide + */ +internal class AppLockConfig(dataDir: File) { + + private val appLockDir = File(dataDir, APP_LOCK_DIR_NAME) + private val appLockConfigFile = File(appLockDir, APP_LOCK_CONFIG_FILE) + + private val appLockDataMap = ArrayMap() + + var appLockTimeout: Long = DEFAULT_TIMEOUT + var biometricsAllowed = DEFAULT_BIOMETRICS_ALLOWED + + init { + appLockDir.mkdirs() + FileUtils.setPermissions(appLockDir, S_IRWXU or S_IRWXG, -1, -1) + } + + /** + * Add an application to [appLockDataMap]. + * + * @param packageName the package name of the application. + * @return true if package was added, false if already exists. + */ + fun addPackage(packageName: String): Boolean { + return appLockDataMap.put(packageName, AppLockData(packageName, false)) == null + } + + /** + * Remove an application from [appLockDataMap]. + * + * @param packageName the package name of the application. + * @return true if package was removed, false otherwise. + */ + fun removePackage(packageName: String): Boolean { + return appLockDataMap.remove(packageName) != null + } + + /** + * Get all the packages protected with app lock. + * + * @return a unique list of package names. + */ + fun getAppLockDataList(): List { + return appLockDataMap.values.toList() + } + + /** + * Check whether a package is protected with app lock. + * + * @return true if package is protected, false otherwise. + */ + fun isPackageProtected(packageName: String): Boolean { + return appLockDataMap.containsKey(packageName) + } + + /** + * Set notifications as protected or not for an application + * in [appLockDataMap]. + * + * @param packageName the package name of the application. + * @return true if config was changed, false otherwise. + */ + fun setShouldRedactNotification(packageName: String, secure: Boolean): Boolean { + return appLockDataMap[packageName]?.let { + appLockDataMap[packageName] = AppLockData( + it.packageName, + secure + ) + true + } ?: run { + Slog.e(TAG, "Attempt to set secure " + + "notification field for package that is not in list") + false + } + } + + /** + * Check whether notifications are protected or not for an application + * in [appLockDataMap]. + * + * @param packageName the package name of the application. + * @return true if notification contents are redacted in app locked state, + * false otherwise. + */ + fun shouldRedactNotification(packageName: String): Boolean { + return appLockDataMap[packageName]?.shouldRedactNotification == true + } + + /** + * Parse contents from [appLockConfigFile]. + */ + fun read() { + reset() + if (!appLockConfigFile.isFile) { + Slog.i(TAG, "No configuration saved") + return + } + try { + appLockConfigFile.inputStream().bufferedReader().use { + val rootObject = JSONObject(it.readText()) + + val version = rootObject.optInt(KEY_VERSION, 0) + migrateData(rootObject, version) + + appLockTimeout = rootObject.optLong(KEY_TIMEOUT, DEFAULT_TIMEOUT) + biometricsAllowed = rootObject.optBoolean(KEY_BIOMETRICS_ALLOWED, DEFAULT_BIOMETRICS_ALLOWED) + val appLockDataList = rootObject.optJSONArray(KEY_APP_LOCK_DATA_LIST) ?: return@use + for (i in 0 until appLockDataList.length()) { + val appLockData = appLockDataList.getJSONObject(i) + val packageName = appLockData.getString(KEY_PACKAGE_NAME) + appLockDataMap[packageName] = AppLockData( + packageName, + appLockData.getBoolean(KEY_REDACT_NOTIFICATION) + ) + } + } + } catch(e: IOException) { + Slog.wtf(TAG, "Failed to read config file", e) + } catch(e: JSONException) { + Slog.wtf(TAG, "Failed to parse config file", e) + } + logD { + "readConfig: data = $appLockDataMap, " + + "timeout = $appLockTimeout" + } + } + + private fun reset() { + appLockDataMap.clear() + appLockTimeout = DEFAULT_TIMEOUT + biometricsAllowed = DEFAULT_BIOMETRICS_ALLOWED + } + + private fun migrateData(jsonData: JSONObject, dataVersion: Int) { + Slog.i(TAG, "Migrating data from version $dataVersion") + when (dataVersion) { + 0 -> { + val packageObject = jsonData.remove(KEY_PACKAGES) as? JSONObject + if (packageObject != null) { + val appLockDataList = JSONArray() + packageObject.keys().forEach { pkg -> + val isSecure = packageObject.getJSONObject(pkg) + .optBoolean(KEY_SECURE_NOTIFICATION, false) + appLockDataList.put( + JSONObject() + .put(KEY_PACKAGE_NAME, pkg) + .put(KEY_REDACT_NOTIFICATION, isSecure) + ) + } + jsonData.put(KEY_APP_LOCK_DATA_LIST, appLockDataList) + } + } + else -> throw IllegalArgumentException("Unknown data version $dataVersion") + } + val nextVersion = dataVersion + 1 + if (nextVersion != CURRENT_VERSION) { + migrateData(jsonData, nextVersion) + } + } + + /** + * Write contents to [appLockConfigFile]. + */ + fun write() { + val rootObject = JSONObject() + try { + rootObject.put(KEY_TIMEOUT, appLockTimeout) + rootObject.put(KEY_BIOMETRICS_ALLOWED, biometricsAllowed) + rootObject.put( + KEY_APP_LOCK_DATA_LIST, + JSONArray( + appLockDataMap.values.map { + JSONObject().apply { + put(KEY_PACKAGE_NAME, it.packageName) + put(KEY_REDACT_NOTIFICATION, it.shouldRedactNotification) + } + } + ) + ) + } catch(e: JSONException) { + Slog.wtf(TAG, "Failed to create json configuration", e) + return + } + try { + appLockConfigFile.outputStream().bufferedWriter().use { + val flattenedString = rootObject.toString(4) + it.write(flattenedString, 0, flattenedString.length) + it.flush() + } + } catch(e: IOException) { + Slog.wtf(TAG, "Failed to write config to file", e) + } + } +} \ No newline at end of file diff --git a/services/core/java/com/android/server/app/AppLockManagerService.kt b/services/core/java/com/android/server/app/AppLockManagerService.kt new file mode 100644 index 000000000000..b51bfdd9708b --- /dev/null +++ b/services/core/java/com/android/server/app/AppLockManagerService.kt @@ -0,0 +1,1122 @@ +/* + * 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 com.android.server.app + +import android.Manifest +import android.annotation.RequiresPermission +import android.app.Activity +import android.app.ActivityManager +import android.app.ActivityManagerInternal +import android.app.ActivityOptions +import android.app.ActivityTaskManager +import android.app.AlarmManager +import android.app.AppLockData +import android.app.AppLockManager +import android.app.IAppLockManagerService +import android.app.KeyguardManager +import android.app.PendingIntent +import android.app.TaskStackListener +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import android.content.IntentSender +import android.content.pm.PackageManager +import android.content.pm.PackageManagerInternal +import android.os.Binder +import android.os.Environment +import android.os.Process +import android.os.RemoteException +import android.os.SystemClock +import android.os.UserHandle +import android.util.ArrayMap +import android.util.ArraySet +import android.util.Log +import android.util.Slog + +import com.android.internal.R +import com.android.internal.annotations.GuardedBy +import com.android.server.LocalServices +import com.android.server.SystemService +import com.android.server.notification.NotificationManagerInternal +import com.android.server.pm.UserManagerInternal +import com.android.server.wm.ActivityInterceptorCallback.ActivityInterceptorInfo +import com.android.server.wm.ActivityTaskManagerInternal + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock +import kotlinx.coroutines.withContext + +internal val TAG = AppLockManagerService::class.simpleName + +private const val ACTION_APP_LOCK_TIMEOUT = "com.android.server.app.AppLockManagerService.APP_LOCK_TIMEOUT" +private const val SETTINGS_PACKAGE = "com.android.settings" + +internal inline fun logD(crossinline msg: () -> String) { + if (Log.isLoggable(TAG, Log.DEBUG)) { + Slog.d(TAG, msg()) + } +} + +/** + * Service to manage per app lock. + * + * @hide + */ +class AppLockManagerService( + private val context: Context +) : IAppLockManagerService.Stub() { + + private val localService = LocalService() + private val serviceScope = CoroutineScope(Dispatchers.Default) + + private val currentUserId: Int + get() = activityManagerInternal.currentUserId + + private var isDeviceSecure = false + + private val mutex = Mutex() + + @GuardedBy("mutex") + private val userConfigMap = ArrayMap() + + @GuardedBy("mutex") + private val topPackages = ArraySet() + + @GuardedBy("mutex") + private val unlockedPackages = ArraySet() + + private val biometricUnlocker: BiometricUnlocker by lazy { + BiometricUnlocker(context) + } + + private val atmInternal: ActivityTaskManagerInternal by lazy { + LocalServices.getService(ActivityTaskManagerInternal::class.java) + } + + private val notificationManagerInternal: NotificationManagerInternal by lazy { + LocalServices.getService(NotificationManagerInternal::class.java) + } + + private val keyguardManager: KeyguardManager by lazy { + context.getSystemService(KeyguardManager::class.java) + } + + private val alarmManager: AlarmManager by lazy { + context.getSystemService(AlarmManager::class.java) + } + + private val userManagerInternal: UserManagerInternal by lazy { + LocalServices.getService(UserManagerInternal::class.java) + } + + private val activityManagerInternal: ActivityManagerInternal by lazy { + LocalServices.getService(ActivityManagerInternal::class.java) + } + + private val packageManager: PackageManager by lazy { + context.packageManager + } + + private val pmInternal: PackageManagerInternal by lazy { + LocalServices.getService(PackageManagerInternal::class.java) + } + + private var deviceLocked = false + + private val alarmsMutex = Mutex() + + @GuardedBy("alarmsMutex") + private val scheduledAlarms = ArrayMap() + + private val whiteListedSystemApps: List by lazy { + val systemPackages = pmInternal.getInstalledApplications( + PackageManager.MATCH_SYSTEM_ONLY.toLong(), + currentUserId, + Process.myUid() + ).map { it.packageName } + context.resources.getStringArray(R.array.config_appLockAllowedSystemApps).filter { + systemPackages.contains(it) + } + } + + private val packageChangeReceiver = object : BroadcastReceiver() { + override fun onReceive(context: Context?, intent: Intent?) { + if (intent?.action != Intent.ACTION_PACKAGE_REMOVED) return + val userId = getSendingUserId() + if (userId != currentUserId) { + logD { + "Ignoring package removal broadcast from user $userId" + } + return + } + val isReplacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false /* defaultValue */) + if (isReplacing) { + logD { + "Ignoring package update broadcast" + } + return + } + val packageName = intent.data?.schemeSpecificPart ?: run { + Slog.e(TAG, "Failed to get package name") + return + } + serviceScope.launch { + val config = mutex.withLock { + userConfigMap[userId] ?: run { + Slog.e(TAG, "Config unavailable for user $userId") + return@launch + } + } + mutex.withLock { + if (!config.isPackageProtected(packageName)) { + logD { + "Package $packageName not in the list, ignoring" + } + return@launch + } + } + logD { + "Package $packageName uninstalled, cleaning up" + } + alarmsMutex.withLock { + scheduledAlarms.remove(packageName)?.let { + alarmManager.cancel(it) + } + } + mutex.withLock { + unlockedPackages.remove(packageName) + if (config.removePackage(packageName)) { + withContext(Dispatchers.IO) { + config.write() + } + } + } + } + } + } + + private val taskStackListener = object : TaskStackListener() { + override fun onTaskStackChanged() { + logD { + "onTaskStackChanged" + } + serviceScope.launch { + val currentTopPackages = atmInternal.topVisibleActivities.map { + it.activityToken + }.filter { + atmInternal.isVisibleActivity(it) + }.map { + atmInternal.getActivityName(it)?.packageName + }.filterNotNull().toSet() + logD { + "currentTopPackages = $currentTopPackages" + } + // We should return early if current top packages + // are empty to avoid doing anything absurd. + if (currentTopPackages.isEmpty()) return@launch + val packagesToLock = mutex.withLock { + logD { + "topPackages = $topPackages" + } + val packages = topPackages.filter { + !currentTopPackages.contains(it) && unlockedPackages.contains(it) + }.toSet() + topPackages.clear() + topPackages.addAll(currentTopPackages) + return@withLock packages + } + packagesToLock.forEach { + scheduleLockAlarm(it) + } + alarmsMutex.withLock { + currentTopPackages.forEach { pkg -> + scheduledAlarms.remove(pkg)?.let { + logD { + "Cancelling timeout alarm for $pkg" + } + alarmManager.cancel(it) + } + } + } + currentTopPackages.forEach { + checkAndUnlockPackage(it) + } + } + } + + override fun onActivityUnpinned() { + logD { + "onActivityUnpinned" + } + onTaskStackChanged() + } + } + + private fun scheduleLockAlarm(pkg: String) { + logD { + "scheduleLockAlarm, package = $pkg" + } + serviceScope.launch { + alarmsMutex.withLock { + if (scheduledAlarms.containsKey(pkg)) { + logD { + "Alarm already scheduled for package $pkg" + } + return@launch + } + } + val timeout = mutex.withLock { + userConfigMap[currentUserId]?.appLockTimeout + } ?: run { + Slog.e(TAG, "Failed to retrieve user config for $currentUserId") + return@launch + } + val pendingIntent = PendingIntent.getBroadcast( + context, + pkg.hashCode(), + Intent(ACTION_APP_LOCK_TIMEOUT).apply { + putExtra(Intent.EXTRA_PACKAGE_NAME, pkg) + }, + PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT + ) + alarmManager.setExactAndAllowWhileIdle( + AlarmManager.ELAPSED_REALTIME_WAKEUP, + SystemClock.elapsedRealtime() + timeout, + pendingIntent + ) + alarmsMutex.withLock { + scheduledAlarms[pkg] = pendingIntent + } + } + } + + private fun checkAndUnlockPackage(pkg: String) { + if (!isDeviceSecure) return + serviceScope.launch { + mutex.withLock { + if (unlockedPackages.contains(pkg)) return@launch + val config = userConfigMap[currentUserId] ?: run { + Slog.e(TAG, "Config unavailable for user $currentUserId") + return@launch + } + if (!config.isPackageProtected(pkg)) return@launch + } + logD { + "$pkg is locked out, asking user to unlock" + } + unlockInternal(pkg, currentUserId, + onSuccess = { + serviceScope.launch { + mutex.withLock { + unlockedPackages.add(pkg) + } + } + }, + onCancel = { + // Send user to home on cancel + context.mainExecutor.execute { + atmInternal.startHomeActivity(currentUserId, + "unlockInternal#onCancel") + } + } + ) + } + } + + private val lockAlarmReceiver = object : BroadcastReceiver() { + override fun onReceive(context: Context?, intent: Intent?) { + if (intent?.action != ACTION_APP_LOCK_TIMEOUT) return + logD { + "Lock alarm received" + } + val packageName = intent.getStringExtra(Intent.EXTRA_PACKAGE_NAME) ?: return + logD { + "$packageName timed out" + } + serviceScope.launch { + mutex.withLock { + if (topPackages.contains(packageName)) { + logD { + "$packageName is currently in foreground, skipping lock" + } + // Mark it as unlocked, since it actually is + unlockedPackages.add(packageName) + return@withLock + } + unlockedPackages.remove(packageName) + } + alarmsMutex.withLock { + scheduledAlarms.remove(packageName) + } + val isContentSecure = mutex.withLock { + userConfigMap[currentUserId]?.shouldRedactNotification(packageName) ?: run { + Slog.e(TAG, "Config unavailable for user $currentUserId") + return@launch + } + } + notificationManagerInternal.updateSecureNotifications( + packageName, + isContentSecure, + true /* isBubbleUpSuppressed */, + currentUserId + ) + } + } + } + + private fun getActualUserId(userId: Int, tag: String): Int { + return ActivityManager.handleIncomingUser(Binder.getCallingPid(), + Binder.getCallingUid(), userId, false /* allowAll */, + true /* requireFull */, tag, AppLockManagerService::class.qualifiedName) + } + + private inline fun clearAndExecute(crossinline block: () -> R): R { + val ident = Binder.clearCallingIdentity() + try { + return block() + } finally { + Binder.restoreCallingIdentity(ident) + } + } + + private fun unlockInternal( + pkg: String, + userId: Int, + onSuccess: () -> Unit, + onCancel: () -> Unit, + ) { + clearAndExecute { + if (!biometricUnlocker.canUnlock()) { + Slog.e(TAG, "Application cannot be unlocked with biometrics or device credentials") + return@clearAndExecute + } + biometricUnlocker.unlock(getLabelForPackage(pkg, userId), onSuccess, onCancel) + } + } + + private fun getLabelForPackage(pkg: String, userId: Int): String? = + try { + pmInternal.getApplicationInfo( + pkg, + PackageManager.MATCH_ALL.toLong(), + Process.myUid(), + userId, + ).loadLabel(packageManager).toString() + } catch(e: PackageManager.NameNotFoundException) { + Slog.e(TAG, "Package $pkg not found") + null + } + + /** + * Add an application to be protected. + * + * @param packageName the package name of the app to add. + * @param userId the user id of the caller. + * @throws [SecurityException] if caller does not have permission + * [Manifest.permissions.MANAGE_APP_LOCK]. + * @throws [IllegalArgumentException] if package is a system app that + * is not whitelisted in [R.array.config_appLockAllowedSystemApps], + * or if package is not installed. + */ + @RequiresPermission(Manifest.permission.MANAGE_APP_LOCK) + override fun addPackage(packageName: String, userId: Int) { + logD { + "addPackage: packageName = $packageName, userId = $userId" + } + enforceCallingPermission("addPackage") + checkPackage(packageName, userId) + val actualUserId = getActualUserId(userId, "addPackage") + serviceScope.launch { + mutex.withLock { + val config = userConfigMap[actualUserId] ?: run { + Slog.e(TAG, "addPackage requested by unknown user id $actualUserId") + return@withLock + } + if (!config.addPackage(packageName)) return@withLock + // Collapse any active notifications or bubbles for the app. + if (!topPackages.contains(packageName)) { + notificationManagerInternal.updateSecureNotifications( + packageName, + true /* isContentSecure */, + true /* isBubbleUpSuppressed */, + actualUserId + ) + } + withContext(Dispatchers.IO) { + config.write() + } + } + } + } + + private fun checkPackage(pkg: String, userId: Int) { + try { + val aInfo = pmInternal.getApplicationInfo( + pkg, + PackageManager.MATCH_ALL.toLong(), + Process.myUid(), + userId + ) + if (!aInfo.isSystemApp()) return + if (!whiteListedSystemApps.contains(pkg)) + throw IllegalArgumentException("System package $pkg is not whitelisted") + } catch(e: PackageManager.NameNotFoundException) { + throw IllegalArgumentException("Package $pkg is not installed") + } + } + + /** + * Remove an application from the protected packages list. + * + * @param packageName the package name of the app to remove. + * @param userId the user id of the caller. + * @throws [SecurityException] if caller does not have permission + * [Manifest.permissions.MANAGE_APP_LOCK]. + */ + @RequiresPermission(Manifest.permission.MANAGE_APP_LOCK) + override fun removePackage(packageName: String, userId: Int) { + logD { + "removePackage: packageName = $packageName, userId = $userId" + } + enforceCallingPermission("removePackage") + val actualUserId = getActualUserId(userId, "removePackage") + serviceScope.launch { + mutex.withLock { + val config = userConfigMap[actualUserId] ?: run { + Slog.e(TAG, "removePackage requested by unknown user id $actualUserId") + return@withLock + } + if (!config.removePackage(packageName)) return@withLock + // Let active notifications be expanded since the app + // is no longer protected. + notificationManagerInternal.updateSecureNotifications( + packageName, + false /* isContentSecure */, + false /* isBubbleUpSuppressed */, + actualUserId + ) + withContext(Dispatchers.IO) { + config.write() + } + } + } + } + + /** + * Get the current auto lock timeout. + * + * @param userId the user id of the caller. + * @return the timeout in milliseconds if configuration for + * current user exists, -1 otherwise. + */ + override fun getTimeout(userId: Int): Long { + logD { + "getTimeout: userId = $userId" + } + val actualUserId = getActualUserId(userId, "getTimeout") + return runBlocking { + mutex.withLock { + userConfigMap[actualUserId]?.let { it.appLockTimeout } ?: run { + Slog.e(TAG, "getTimeout requested by unknown user id $actualUserId") + -1L + } + } + } + } + + /** + * Set auto lock timeout. + * + * @param timeout the timeout in milliseconds. Must be >= 5. + * @param userId the user id of the caller. + * @throws [SecurityException] if caller does not have permission + * [Manifest.permissions.MANAGE_APP_LOCK]. + */ + @RequiresPermission(Manifest.permission.MANAGE_APP_LOCK) + override fun setTimeout(timeout: Long, userId: Int) { + logD { + "setTimeout: timeout = $timeout, userId = $userId" + } + if (timeout < 5L) { + throw IllegalArgumentException("Timeout must be greater than or equal to 5") + } + enforceCallingPermission("setTimeout") + val actualUserId = getActualUserId(userId, "setTimeout") + serviceScope.launch { + mutex.withLock { + val config = userConfigMap[actualUserId] ?: run { + Slog.e(TAG, "setTimeout requested by unknown user id $actualUserId") + return@withLock + } + if (config.appLockTimeout == timeout) return@withLock + config.appLockTimeout = timeout + withContext(Dispatchers.IO) { + config.write() + } + } + } + } + + /** + * Get all the packages protected with app lock. + * + * @param userId the user id of the caller. + * @return list of [AppLockData] of the protected apps. + * @throws [SecurityException] if caller does not have permission + * [Manifest.permissions.MANAGE_APP_LOCK]. + */ + @RequiresPermission(Manifest.permission.MANAGE_APP_LOCK) + override fun getPackageData(userId: Int): List { + logD { + "getPackages: userId = $userId" + } + enforceCallingPermission("getPackages") + val actualUserId = getActualUserId(userId, "getPackages") + return runBlocking { + mutex.withLock { + userConfigMap[actualUserId]?.getAppLockDataList() ?: run { + Slog.e(TAG, "getPackages requested by unknown user id $actualUserId") + emptyList() + } + } + } + } + + /** + * Set whether notification content should be redacted for a package + * in locked state. + * + * @param packageName the package name. + * @param secure true to hide notification content. + * @param userId the user id of the caller. + * @throws [SecurityException] if caller does not have permission + * [Manifest.permissions.MANAGE_APP_LOCK]. + */ + @RequiresPermission(Manifest.permission.MANAGE_APP_LOCK) + override fun setShouldRedactNotification( + packageName: String, + secure: Boolean, + userId: Int, + ) { + logD { + "setShouldRedactNotification: packageName = $packageName, userId = $userId" + } + enforceCallingPermission("setShouldRedactNotification") + val actualUserId = getActualUserId(userId, "setShouldRedactNotification") + serviceScope.launch { + mutex.withLock { + val config = userConfigMap[actualUserId] ?: run { + Slog.e(TAG, "setShouldRedactNotification requested by unknown " + + "user id $actualUserId") + return@withLock + } + if (!config.setShouldRedactNotification(packageName, secure)) return@withLock + val isLocked = !unlockedPackages.contains(packageName) + && !topPackages.contains(packageName) + val shouldSecureContent = secure && isLocked + notificationManagerInternal.updateSecureNotifications( + packageName, + shouldSecureContent, + isLocked /* isBubbleUpSuppressed */, + actualUserId + ) + withContext(Dispatchers.IO) { + config.write() + } + } + } + } + + /** + * Set whether to allow unlocking with biometrics. + * + * @param biometricsAllowed whether to use biometrics. + * @param userId the user id of the caller. + * @throws [SecurityException] if caller does not have permission + * [Manifest.permissions.MANAGE_APP_LOCK]. + */ + @RequiresPermission(Manifest.permission.MANAGE_APP_LOCK) + override fun setBiometricsAllowed(biometricsAllowed: Boolean, userId: Int) { + logD { + "setBiometricsAllowed: biometricsAllowed = $biometricsAllowed, userId = $userId" + } + enforceCallingPermission("setBiometricsAllowed") + val actualUserId = getActualUserId(userId, "setBiometricsAllowed") + serviceScope.launch { + mutex.withLock { + val config = userConfigMap[actualUserId] ?: run { + Slog.e(TAG, "setBiometricsAllowed requested by unknown user id $actualUserId") + return@withLock + } + if (config.biometricsAllowed == biometricsAllowed) return@withLock + config.biometricsAllowed = biometricsAllowed + biometricUnlocker.biometricsAllowed = biometricsAllowed + withContext(Dispatchers.IO) { + config.write() + } + } + } + } + + /** + * Check whether biometrics is allowed for unlocking. + * + * @return true if biometrics will be used for unlocking, false otheriwse. + */ + override fun isBiometricsAllowed(userId: Int): Boolean { + logD { + "isBiometricsAllowed: userId = $userId" + } + val actualUserId = getActualUserId(userId, "isBiometricsAllowed") + return runBlocking { + mutex.withLock { + userConfigMap[actualUserId]?.let { it.biometricsAllowed } ?: run { + Slog.e(TAG, "isBiometricsAllowed requested by unknown user id $actualUserId") + AppLockManager.DEFAULT_BIOMETRICS_ALLOWED + } + } + } + } + + /** + * 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. + * @param userId the user id of the caller. + * @throws [SecurityException] if caller does not have permission + * [Manifest.permissions.MANAGE_APP_LOCK]. + */ + @RequiresPermission(Manifest.permission.MANAGE_APP_LOCK) + override fun unlockPackage(packageName: String, userId: Int) { + logD { + "unlockPackage: packageName = $packageName, userId = $userId" + } + enforceCallingPermission("unlockPackage") + val actualUserId = getActualUserId(userId, "unlockPackage") + serviceScope.launch { + mutex.withLock { + val config = userConfigMap[actualUserId] ?: run { + Slog.e(TAG, "unlockPackage requested by unknown user id $actualUserId") + return@launch + } + if (!config.isPackageProtected(packageName)) { + Slog.w(TAG, "Unlock requested for package $packageName " + + "that is not in list") + return@launch + } + unlockedPackages.add(packageName) + } + notificationManagerInternal.updateSecureNotifications( + packageName, + false /* isContentSecure */, + false /* isBubbleUpSuppressed */, + actualUserId + ) + } + } + + private fun enforceCallingPermission(msg: String) { + context.enforceCallingPermission(Manifest.permission.MANAGE_APP_LOCK, msg) + } + + private fun onStart() { + LocalServices.addService(AppLockManagerServiceInternal::class.java, localService) + } + + private fun onBootCompleted() { + Slog.i(TAG, "onBootCompleted") + context.registerReceiverAsUser( + lockAlarmReceiver, + UserHandle.SYSTEM, + IntentFilter(ACTION_APP_LOCK_TIMEOUT), + null /* broadcastPermission */, + null /* scheduler */, + ) + + context.registerReceiverForAllUsers( + packageChangeReceiver, + IntentFilter(Intent.ACTION_PACKAGE_REMOVED).apply { + addDataScheme(IntentFilter.SCHEME_PACKAGE) + }, + null /* broadcastPermission */, + null /* scheduler */, + ) + + ActivityTaskManager.getService().registerTaskStackListener(taskStackListener) + } + + private fun onUserStarting(userId: Int) { + Slog.i(TAG, "onUserStarting: userId = $userId") + isDeviceSecure = keyguardManager.isDeviceSecure(userId) + logD { + "isDeviceSecure = $isDeviceSecure" + } + serviceScope.launch { + mutex.withLock { + if (userConfigMap.containsKey(userId)) return@withLock + withContext(Dispatchers.IO) { + val config = AppLockConfig(Environment.getDataSystemDeDirectory(userId)) + userConfigMap[userId] = config + config.read() + biometricUnlocker.biometricsAllowed = config.biometricsAllowed + verifyPackagesLocked(config) + } + } + } + } + + private fun verifyPackagesLocked(config: AppLockConfig) { + val currentPackages = config.getAppLockDataList().map { it.packageName } + var size = currentPackages.size + if (size == 0) return + val installedPackages = pmInternal.getInstalledApplications( + PackageManager.MATCH_ALL.toLong(), + currentUserId, + Process.myUid() + ).map { it.packageName } + var changed = false + logD { + "Current packages = $currentPackages" + } + for (i in 0 until size) { + val pkg = currentPackages[i] + if (!installedPackages.contains(pkg)) { + config.removePackage(pkg) + size-- + changed = true + } + } + logD { + val filteredPackages = config.getAppLockDataList().map { it.packageName } + "Filtered packages = $filteredPackages" + } + if (changed) { + config.write() + } + } + + private fun onUserStopping(userId: Int): Job { + Slog.i(TAG, "onUserStopping: userId = $userId") + return serviceScope.launch { + mutex.withLock { + unlockedPackages.clear() + userConfigMap[userId]?.let { + withContext(Dispatchers.IO) { + it.write() + } + } + } + } + } + + private fun onUserSwitching(oldUserId: Int, newUserId: Int) { + Slog.i(TAG, "onUserSwitching: oldUserId = $oldUserId, newUserId = $newUserId") + serviceScope.launch { + if (oldUserId != UserHandle.USER_NULL) { + onUserStopping(oldUserId).join() + } + onUserStarting(newUserId) + } + } + + private inner class LocalService : AppLockManagerServiceInternal { + /** + * Check whether user is valid and device is secure + */ + private fun checkUserAndDeviceStatus(userId: Int): Boolean { + if (userId < 0) { + logD { + "Ignoring requireUnlock call for special user $userId" + } + return false + } + if (!isDeviceSecure) { + logD { + "Device is not secure, app does not require unlock" + } + return false + } + val isManaged = clearAndExecute { + userManagerInternal.isUserManaged(userId) + } + if (isManaged) { + logD { + "User id $userId belongs to a work profile, ignoring requireUnlock" + } + } + return !isManaged + } + + override fun requireUnlock(packageName: String, userId: Int): Boolean { + return requireUnlockInternal(packageName, userId, false /* ignoreLockState */) + } + + private fun requireUnlockInternal( + packageName: String, + userId: Int, + ignoreLockState: Boolean, + ) : Boolean { + if (!checkUserAndDeviceStatus(userId)) return false + val isLocked = clearAndExecute { + // If device is locked then there is no point in proceeding. + !ignoreLockState && keyguardManager.isDeviceLocked() + } + if (isLocked) { + logD { + "Device is locked, app does not require unlock" + } + return false + } + logD { + "requireUnlock: packageName = $packageName" + } + val actualUserId = getActualUserId(userId, "requireUnlock") + return runBlocking { + mutex.withLock { + val config = userConfigMap[actualUserId] ?: run { + Slog.e(TAG, "requireUnlock queried by unknown user id $actualUserId") + return@withLock false + } + val requireUnlock = config.isPackageProtected(packageName) && + !unlockedPackages.contains(packageName) + logD { + "requireUnlock = $requireUnlock" + } + return@withLock requireUnlock + } + } + } + + override fun reportPasswordChanged(userId: Int) { + logD { + "reportPasswordChanged: userId = $userId" + } + if (userId != currentUserId) { + logD { + "Ignoring password change event for user $userId" + } + return + } + isDeviceSecure = keyguardManager.isDeviceSecure(userId) + logD { + "isDeviceSecure = $isDeviceSecure" + } + } + + override fun shouldRedactNotification( + packageName: String, + userId: Int, + ) : Boolean { + if (!checkUserAndDeviceStatus(userId)) return false + logD { + "shouldRedactNotification: packageName = $packageName, userId = $userId" + } + val actualUserId = getActualUserId(userId, "shouldRedactNotification") + if (!requireUnlockInternal(packageName, userId, true /* ignoreLockState */)) return false + return runBlocking { + mutex.withLock { + val config = userConfigMap[actualUserId] ?: run { + Slog.e(TAG, "shouldRedactNotification queried by " + + "unknown user id $actualUserId") + return@withLock false + } + val secure = config.shouldRedactNotification(packageName) + logD { + "Secure = $secure" + } + return@withLock secure + } + } + } + + override fun notifyDeviceLocked(locked: Boolean, userId: Int) { + logD { + "Device locked = $locked for user $userId" + } + if (userId != currentUserId || + !isDeviceSecure || + deviceLocked == locked) return + deviceLocked = locked + serviceScope.launch { + val config = mutex.withLock { + userConfigMap[currentUserId] ?: run { + Slog.e(TAG, "Config unavailable for user $currentUserId") + return@launch + } + } + if (deviceLocked) { + mutex.withLock { + if (unlockedPackages.isEmpty()) return@withLock + logD { + "Locking all packages" + } + unlockedPackages.clear() + } + alarmsMutex.withLock { + if (scheduledAlarms.isEmpty()) return@withLock + scheduledAlarms.values.forEach { + alarmManager.cancel(it) + } + scheduledAlarms.clear() + } + } else { + mutex.withLock { + if (topPackages.isEmpty()) return@withLock + // If device is locked with an app in the foreground, + // even if it is removed from [unlockedPackages], it will + // still be shown when unlocked, so we need to start home + // activity as soon as such a condition is detected on unlock. + val shouldGoToHome = topPackages.any { + config.isPackageProtected(it) && + !unlockedPackages.contains(it) + } + if (!shouldGoToHome) return@withLock + logD { + "Locking foreground package" + } + context.mainExecutor.execute { + atmInternal.startHomeActivity(currentUserId, + "Locked package in foreground") + } + } + } + } + } + + override fun interceptActivity(info: ActivityInterceptorInfo): Intent? { + val packageName = info.aInfo.packageName + logD { + "interceptActivity, pkg = $packageName" + } + if (!localService.requireUnlock(packageName, info.userId)) return null + val target = IntentSender( + atmInternal.getIntentSender( + ActivityManager.INTENT_SENDER_ACTIVITY, + info.callingPackage, + info.callingFeatureId, + info.realCallingUid, + info.userId, + null /* token */, + null /* resultCode */, + 0 /* requestCode */, + arrayOf(info.intent), + arrayOf(info.resolvedType), + PendingIntent.FLAG_CANCEL_CURRENT or + PendingIntent.FLAG_ONE_SHOT or + PendingIntent.FLAG_IMMUTABLE, + ActivityOptions.makeBasic().toBundle() + ) + ) + val intent = Intent(AppLockManager.ACTION_UNLOCK_APP) + .setPackage(SETTINGS_PACKAGE) + .apply { + putExtra(Intent.EXTRA_PACKAGE_NAME, packageName) + putExtra(Intent.EXTRA_INTENT, target) + putExtra(Intent.EXTRA_USER_ID, info.userId) + putExtra(AppLockManager.EXTRA_PACKAGE_LABEL, info.aInfo.loadLabel(packageManager)) + putExtra(AppLockManager.EXTRA_ALLOW_BIOMETRICS, isBiometricsAllowed(info.userId)) + } + return intent + } + } + + class Lifecycle(context: Context) : SystemService(context) { + private val service = AppLockManagerService(context) + + override fun onStart() { + publishBinderService(Context.APP_LOCK_SERVICE, service) + service.onStart() + } + + override fun onBootPhase(phase: Int) { + if (phase == PHASE_ACTIVITY_MANAGER_READY) { + service.onBootCompleted() + } + } + + override fun onUserStarting(user: TargetUser) { + service.onUserStarting(user.userIdentifier) + } + + override fun onUserStopping(user: TargetUser) { + service.onUserStopping(user.userIdentifier) + } + + override fun onUserSwitching(from: TargetUser?, to: TargetUser) { + service.onUserSwitching( + from?.userIdentifier ?: UserHandle.USER_NULL, + to.userIdentifier + ) + } + } +} + +/** + * Internal class for system server to manage app lock. + * + * @hide + */ +interface AppLockManagerServiceInternal { + + /** + * Whether user has to unlock this application in order to + * open it. + * + * @param packageName the package name of the app to check. + * @param userId the user id given by the caller. + * @return true if user has to unlock, false otherwise. + */ + fun requireUnlock(packageName: String, userId: Int): Boolean + + /** + * Report that password for user has changed. + * + * @param userId the user for which password has changed. + */ + fun reportPasswordChanged(userId: Int) + + /** + * Check whether notification content should be hidden for a package. + * + * @param packageName the package to check for. + * @param userId the user id given by the caller. + * @return true if notification should be hidden, false otherwise. + */ + fun shouldRedactNotification(packageName: String, userId: Int): Boolean + + /** + * Notify that the device is locked for current user. + */ + fun notifyDeviceLocked(locked: Boolean, userId: Int) + + /** + * Whether to intercept the activity launch from a package. Used + * to show confirm credentials prompt. + * + * @param info [ActivityInterceptorInfo] of intercepted activity. + * @return [Intent] which will be fired. Return null if activity + * shouldn't be intercepted. + */ + fun interceptActivity(info: ActivityInterceptorInfo): Intent? +} \ No newline at end of file diff --git a/services/core/java/com/android/server/app/BiometricUnlocker.kt b/services/core/java/com/android/server/app/BiometricUnlocker.kt new file mode 100644 index 000000000000..0d5e92355706 --- /dev/null +++ b/services/core/java/com/android/server/app/BiometricUnlocker.kt @@ -0,0 +1,108 @@ +/* + * 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 com.android.server.app + +import android.content.Context +import android.hardware.biometrics.BiometricConstants +import android.hardware.biometrics.BiometricManager +import android.hardware.biometrics.BiometricManager.Authenticators +import android.hardware.biometrics.BiometricPrompt +import android.hardware.biometrics.BiometricPrompt.AuthenticationCallback +import android.hardware.biometrics.BiometricPrompt.AuthenticationResult +import android.os.CancellationSignal +import android.util.Slog + +import com.android.internal.R + +/** + * Handles logic of unlocking an app with biometrics or device credentials. + * + * @hide + */ +internal class BiometricUnlocker(private val context: Context) { + + private val biometricManager = context.getSystemService(BiometricManager::class.java) + + // Set operation must be externally synchronized + var biometricsAllowed = false + + /** + * Determine whether biometrics or device credentials can be used for + * unlocking operation. + */ + fun canUnlock(): Boolean = + biometricManager.canAuthenticate( + Authenticators.BIOMETRIC_STRONG or + Authenticators.DEVICE_CREDENTIAL + ) == BiometricManager.BIOMETRIC_SUCCESS + + /** + * Unlock an application. Should call this method only if + * [canUnlock] returned true. + * + * @param title the title of the dialog prompt. + * @param onSuccess the callback invoked on successfull authentication. + * @param onCancel the callback invoked when authentication is cancelled. + */ + fun unlock( + packageLabel: String?, + onSuccess: () -> Unit, + onCancel: () -> Unit, + ) { + val callback = object : AuthenticationCallback() { + override fun onAuthenticationSucceeded(result: AuthenticationResult) { + logD { + "onAuthenticationSucceeded" + } + onSuccess() + } + + override fun onAuthenticationError(errorCode: Int, errString: CharSequence) { + Slog.i(TAG, "onAuthenticationError, errorCode = " + + "$errorCode, errString = $errString") + if (errorCode == BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED) { + onCancel() + } + } + } + showCredentialsPrompt( + context.getString(R.string.unlock_application, packageLabel), + callback + ) + } + + private fun showCredentialsPrompt( + title: String, + callback: AuthenticationCallback, + ) { + var authenticators = Authenticators.DEVICE_CREDENTIAL + if (biometricsAllowed) { + authenticators = authenticators or Authenticators.BIOMETRIC_STRONG + } + val prompt = BiometricPrompt.Builder(context) + .setTitle(title) + .setAllowedAuthenticators(authenticators) + .setAllowBackgroundAuthentication(true) + .build() + prompt.authenticateUser( + CancellationSignal(), + context.mainExecutor, + callback, + context.userId, + ) + } +} \ No newline at end of file diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java index e31c952e10f9..2902fc353850 100644 --- a/services/core/java/com/android/server/appop/AppOpsService.java +++ b/services/core/java/com/android/server/appop/AppOpsService.java @@ -3865,18 +3865,18 @@ private SyncNotedAppOp startOperationImpl(@NonNull IBinder clientId, int code, i } @Override - public SyncNotedAppOp startProxyOperation(int code, + public SyncNotedAppOp startProxyOperation(@NonNull IBinder clientId, int code, @NonNull AttributionSource attributionSource, boolean startIfModeDefault, boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage, boolean skipProxyOperation, @AttributionFlags int proxyAttributionFlags, @AttributionFlags int proxiedAttributionFlags, int attributionChainId) { - return mCheckOpsDelegateDispatcher.startProxyOperation(code, attributionSource, + return mCheckOpsDelegateDispatcher.startProxyOperation(clientId, code, attributionSource, startIfModeDefault, shouldCollectAsyncNotedOp, message, shouldCollectMessage, skipProxyOperation, proxyAttributionFlags, proxiedAttributionFlags, attributionChainId); } - private SyncNotedAppOp startProxyOperationImpl(int code, + private SyncNotedAppOp startProxyOperationImpl(@NonNull IBinder clientId, int code, @NonNull AttributionSource attributionSource, boolean startIfModeDefault, boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage, boolean skipProxyOperation, @AttributionFlags @@ -3885,11 +3885,9 @@ private SyncNotedAppOp startProxyOperationImpl(int code, final int proxyUid = attributionSource.getUid(); final String proxyPackageName = attributionSource.getPackageName(); final String proxyAttributionTag = attributionSource.getAttributionTag(); - final IBinder proxyToken = attributionSource.getToken(); final int proxiedUid = attributionSource.getNextUid(); final String proxiedPackageName = attributionSource.getNextPackageName(); final String proxiedAttributionTag = attributionSource.getNextAttributionTag(); - final IBinder proxiedToken = attributionSource.getNextToken(); verifyIncomingProxyUid(attributionSource); verifyIncomingOp(code); @@ -3928,7 +3926,7 @@ private SyncNotedAppOp startProxyOperationImpl(int code, if (!skipProxyOperation) { // Test if the proxied operation will succeed before starting the proxy operation - final SyncNotedAppOp testProxiedOp = startOperationUnchecked(proxiedToken, code, + final SyncNotedAppOp testProxiedOp = startOperationUnchecked(clientId, code, proxiedUid, resolvedProxiedPackageName, proxiedAttributionTag, proxyUid, resolvedProxyPackageName, proxyAttributionTag, proxiedFlags, startIfModeDefault, shouldCollectAsyncNotedOp, message, shouldCollectMessage, @@ -3940,7 +3938,7 @@ private SyncNotedAppOp startProxyOperationImpl(int code, final int proxyFlags = isProxyTrusted ? AppOpsManager.OP_FLAG_TRUSTED_PROXY : AppOpsManager.OP_FLAG_UNTRUSTED_PROXY; - final SyncNotedAppOp proxyAppOp = startOperationUnchecked(proxyToken, code, proxyUid, + final SyncNotedAppOp proxyAppOp = startOperationUnchecked(clientId, code, proxyUid, resolvedProxyPackageName, proxyAttributionTag, Process.INVALID_UID, null, null, proxyFlags, startIfModeDefault, !isProxyTrusted, "proxy " + message, shouldCollectMessage, proxyAttributionFlags, attributionChainId, @@ -3950,7 +3948,7 @@ private SyncNotedAppOp startProxyOperationImpl(int code, } } - return startOperationUnchecked(proxiedToken, code, proxiedUid, resolvedProxiedPackageName, + return startOperationUnchecked(clientId, code, proxiedUid, resolvedProxiedPackageName, proxiedAttributionTag, proxyUid, resolvedProxyPackageName, proxyAttributionTag, proxiedFlags, startIfModeDefault, shouldCollectAsyncNotedOp, message, shouldCollectMessage, proxiedAttributionFlags, attributionChainId, @@ -4091,22 +4089,20 @@ private void finishOperationImpl(IBinder clientId, int code, int uid, String pac } @Override - public void finishProxyOperation(int code, @NonNull AttributionSource attributionSource, - boolean skipProxyOperation) { - mCheckOpsDelegateDispatcher.finishProxyOperation(code, attributionSource, + public void finishProxyOperation(@NonNull IBinder clientId, int code, + @NonNull AttributionSource attributionSource, boolean skipProxyOperation) { + mCheckOpsDelegateDispatcher.finishProxyOperation(clientId, code, attributionSource, skipProxyOperation); } - private Void finishProxyOperationImpl(int code, @NonNull AttributionSource attributionSource, - boolean skipProxyOperation) { + private Void finishProxyOperationImpl(IBinder clientId, int code, + @NonNull AttributionSource attributionSource, boolean skipProxyOperation) { final int proxyUid = attributionSource.getUid(); final String proxyPackageName = attributionSource.getPackageName(); final String proxyAttributionTag = attributionSource.getAttributionTag(); - final IBinder proxyToken = attributionSource.getToken(); final int proxiedUid = attributionSource.getNextUid(); final String proxiedPackageName = attributionSource.getNextPackageName(); final String proxiedAttributionTag = attributionSource.getNextAttributionTag(); - final IBinder proxiedToken = attributionSource.getNextToken(); skipProxyOperation = skipProxyOperation && isCallerAndAttributionTrusted(attributionSource); @@ -4123,7 +4119,7 @@ private Void finishProxyOperationImpl(int code, @NonNull AttributionSource attri } if (!skipProxyOperation) { - finishOperationUnchecked(proxyToken, code, proxyUid, resolvedProxyPackageName, + finishOperationUnchecked(clientId, code, proxyUid, resolvedProxyPackageName, proxyAttributionTag); } @@ -4133,7 +4129,7 @@ private Void finishProxyOperationImpl(int code, @NonNull AttributionSource attri return null; } - finishOperationUnchecked(proxiedToken, code, proxiedUid, resolvedProxiedPackageName, + finishOperationUnchecked(clientId, code, proxiedUid, resolvedProxiedPackageName, proxiedAttributionTag); return null; @@ -7552,7 +7548,6 @@ public void collectNoteOpCallsForValidation(String stackTrace, int op, String pa Objects.requireNonNull(stackTrace); Preconditions.checkArgument(op >= 0); Preconditions.checkArgument(op < AppOpsManager._NUM_OP); - Objects.requireNonNull(version); NoteOpTrace noteOpTrace = new NoteOpTrace(stackTrace, op, packageName, version); @@ -7726,42 +7721,42 @@ private SyncNotedAppOp startDelegateOperationImpl(IBinder token, int code, int u attributionFlags, attributionChainId, AppOpsService.this::startOperationImpl); } - public SyncNotedAppOp startProxyOperation(int code, + public SyncNotedAppOp startProxyOperation(@NonNull IBinder clientId, int code, @NonNull AttributionSource attributionSource, boolean startIfModeDefault, boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage, boolean skipProxyOperation, @AttributionFlags int proxyAttributionFlags, @AttributionFlags int proxiedAttributionFlags, int attributionChainId) { if (mPolicy != null) { if (mCheckOpsDelegate != null) { - return mPolicy.startProxyOperation(code, attributionSource, + return mPolicy.startProxyOperation(clientId, code, attributionSource, startIfModeDefault, shouldCollectAsyncNotedOp, message, shouldCollectMessage, skipProxyOperation, proxyAttributionFlags, proxiedAttributionFlags, attributionChainId, this::startDelegateProxyOperationImpl); } else { - return mPolicy.startProxyOperation(code, attributionSource, + return mPolicy.startProxyOperation(clientId, code, attributionSource, startIfModeDefault, shouldCollectAsyncNotedOp, message, shouldCollectMessage, skipProxyOperation, proxyAttributionFlags, proxiedAttributionFlags, attributionChainId, AppOpsService.this::startProxyOperationImpl); } } else if (mCheckOpsDelegate != null) { - return startDelegateProxyOperationImpl(code, attributionSource, + return startDelegateProxyOperationImpl(clientId, code, attributionSource, startIfModeDefault, shouldCollectAsyncNotedOp, message, shouldCollectMessage, skipProxyOperation, proxyAttributionFlags, proxiedAttributionFlags, attributionChainId); } - return startProxyOperationImpl(code, attributionSource, startIfModeDefault, + return startProxyOperationImpl(clientId, code, attributionSource, startIfModeDefault, shouldCollectAsyncNotedOp, message, shouldCollectMessage, skipProxyOperation, proxyAttributionFlags, proxiedAttributionFlags, attributionChainId); } - private SyncNotedAppOp startDelegateProxyOperationImpl(int code, + private SyncNotedAppOp startDelegateProxyOperationImpl(@NonNull IBinder clientId, int code, @NonNull AttributionSource attributionSource, boolean startIfModeDefault, boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage, boolean skipProxyOperation, @AttributionFlags int proxyAttributionFlags, @AttributionFlags int proxiedAttributionFlsgs, int attributionChainId) { - return mCheckOpsDelegate.startProxyOperation(code, attributionSource, + return mCheckOpsDelegate.startProxyOperation(clientId, code, attributionSource, startIfModeDefault, shouldCollectAsyncNotedOp, message, shouldCollectMessage, skipProxyOperation, proxyAttributionFlags, proxiedAttributionFlsgs, attributionChainId, AppOpsService.this::startProxyOperationImpl); @@ -7790,27 +7785,28 @@ private void finishDelegateOperationImpl(IBinder clientId, int code, int uid, AppOpsService.this::finishOperationImpl); } - public void finishProxyOperation(int code, + public void finishProxyOperation(@NonNull IBinder clientId, int code, @NonNull AttributionSource attributionSource, boolean skipProxyOperation) { if (mPolicy != null) { if (mCheckOpsDelegate != null) { - mPolicy.finishProxyOperation(code, attributionSource, + mPolicy.finishProxyOperation(clientId, code, attributionSource, skipProxyOperation, this::finishDelegateProxyOperationImpl); } else { - mPolicy.finishProxyOperation(code, attributionSource, + mPolicy.finishProxyOperation(clientId, code, attributionSource, skipProxyOperation, AppOpsService.this::finishProxyOperationImpl); } } else if (mCheckOpsDelegate != null) { - finishDelegateProxyOperationImpl(code, attributionSource, skipProxyOperation); + finishDelegateProxyOperationImpl(clientId, code, attributionSource, + skipProxyOperation); } else { - finishProxyOperationImpl(code, attributionSource, skipProxyOperation); + finishProxyOperationImpl(clientId, code, attributionSource, skipProxyOperation); } } - private Void finishDelegateProxyOperationImpl(int code, + private Void finishDelegateProxyOperationImpl(@NonNull IBinder clientId, int code, @NonNull AttributionSource attributionSource, boolean skipProxyOperation) { - mCheckOpsDelegate.finishProxyOperation(code, attributionSource, skipProxyOperation, - AppOpsService.this::finishProxyOperationImpl); + mCheckOpsDelegate.finishProxyOperation(clientId, code, attributionSource, + skipProxyOperation, AppOpsService.this::finishProxyOperationImpl); return null; } } diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 0b6b89074cfc..06a6c288757f 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -169,6 +169,7 @@ import android.view.accessibility.AccessibilityManager; import android.widget.Toast; +import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.DumpUtils; @@ -648,7 +649,6 @@ public void onError(int error) { // Devices for which the volume is fixed (volume is either max or muted) Set mFixedVolumeDevices = new HashSet<>(Arrays.asList( - AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET, AudioSystem.DEVICE_OUT_AUX_LINE)); // Devices for which the volume is always max, no volume panel Set mFullVolumeDevices = new HashSet<>(Arrays.asList( @@ -837,6 +837,9 @@ protected boolean removeEldestEntry(Map.Entry eldest) { 0.9f, // Pre-scale for index 3 }; + private boolean mLinkNotificationWithVolume; + private final boolean mVoiceCapable; + private NotificationManager mNm; private AudioManagerInternal.RingerModeDelegate mRingerModeDelegate; private VolumePolicy mVolumePolicy = VolumePolicy.DEFAULT; @@ -885,6 +888,9 @@ protected boolean removeEldestEntry(Map.Entry eldest) { private @AttributeSystemUsage int[] mSupportedSystemUsages = new int[]{AudioAttributes.USAGE_CALL_ASSISTANT}; + // Alert Slider + private boolean mHasAlertSlider = false; + // Defines the format for the connection "address" for ALSA devices public static String makeAlsaAddressString(int card, int device) { return "card=" + card + ";device=" + device + ";"; @@ -1021,6 +1027,10 @@ public AudioService(Context context, AudioSystemAdapter audioSystem, mNotifAliasRing = mContext.getResources().getBoolean( com.android.internal.R.bool.config_alias_ring_notif_stream_types); + mHasAlertSlider = mContext.getResources().getBoolean(R.bool.config_hasAlertSlider) + && !TextUtils.isEmpty(mContext.getResources().getString(R.string.alert_slider_state_path)) + && !TextUtils.isEmpty(mContext.getResources().getString(R.string.alert_slider_uevent_match_path)); + // Initialize volume // Priority 1 - Android Property // Priority 2 - Audio Policy Service @@ -1156,6 +1166,9 @@ public AudioService(Context context, AudioSystemAdapter audioSystem, } } + mVoiceCapable = context.getResources().getBoolean( + com.android.internal.R.bool.config_voice_capable); + if (looper == null) { createAudioSystemThread(); } else { @@ -1185,6 +1198,10 @@ public AudioService(Context context, AudioSystemAdapter audioSystem, mSafeMediaVolumeIndex = mContext.getResources().getInteger( com.android.internal.R.integer.config_safe_media_volume_index) * 10; + // read this in before readPersistedSettings() because updateStreamVolumeAlias needs it + mLinkNotificationWithVolume = Settings.Secure.getInt(mContext.getContentResolver(), + Settings.Secure.VOLUME_LINK_NOTIFICATION, 1) == 1; + mUseFixedVolume = mContext.getResources().getBoolean( com.android.internal.R.bool.config_useFixedVolume); @@ -2173,6 +2190,15 @@ private void updateStreamVolumeAlias(boolean updateVolumes, String caller) { mStreamVolumeAlias[AudioSystem.STREAM_ACCESSIBILITY] = a11yStreamAlias; mStreamVolumeAlias[AudioSystem.STREAM_ASSISTANT] = assistantStreamAlias; + if (mVoiceCapable) { + if (mLinkNotificationWithVolume) { + mStreamVolumeAlias[AudioSystem.STREAM_NOTIFICATION] = AudioSystem.STREAM_RING; + } else { + mStreamVolumeAlias[AudioSystem.STREAM_NOTIFICATION] = + AudioSystem.STREAM_NOTIFICATION; + } + } + if (updateVolumes && mStreamStates != null) { updateDefaultVolumes(); @@ -2628,6 +2654,9 @@ private void readPersistedSettings() { AudioSystem.setRttEnabled(mRttEnabled); } + mLinkNotificationWithVolume = Settings.Secure.getInt(cr, + Settings.Secure.VOLUME_LINK_NOTIFICATION, 1) == 1; + mMuteAffectedStreams = mSettings.getSystemIntForUser(cr, System.MUTE_STREAMS_AFFECTED, AudioSystem.DEFAULT_MUTE_STREAMS_AFFECTED, UserHandle.USER_CURRENT); @@ -3082,6 +3111,27 @@ private void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, } } + if (mHasAlertSlider) { + int volumeType = mStreamVolumeAlias[streamType]; + VolumeStreamState volumeState = mStreamStates[volumeType]; + int state = getDeviceForStream(volumeType); + int index = volumeState.getIndex(state); + int ringerMode = getRingerModeInternal(); + if ((volumeType == AudioSystem.STREAM_RING) + && (direction == AudioManager.ADJUST_LOWER) + && (index == 0)) { + direction = AudioManager.ADJUST_SAME; + } + if ((ringerMode == AudioManager.RINGER_MODE_SILENT) + && (direction == AudioManager.ADJUST_RAISE + && volumeType != AudioSystem.STREAM_MUSIC + && volumeType != AudioSystem.STREAM_ALARM + && volumeType != AudioSystem.STREAM_VOICE_CALL + && !isInCommunication())) { + direction = AudioManager.ADJUST_SAME; + } + } + final boolean isMute = isMuteAdjust(direction); ensureValidStreamType(streamType); @@ -8536,6 +8586,8 @@ private class SettingsObserver extends ContentObserver { Settings.System.MASTER_MONO), false, this); mContentResolver.registerContentObserver(Settings.System.getUriFor( Settings.System.MASTER_BALANCE), false, this); + mContentResolver.registerContentObserver(Settings.Secure.getUriFor( + Settings.Secure.VOLUME_LINK_NOTIFICATION), false, this); mEncodedSurroundMode = mSettings.getGlobalInt( mContentResolver, Settings.Global.ENCODED_SURROUND_OUTPUT, @@ -8572,6 +8624,14 @@ public void onChange(boolean selfChange) { updateEncodedSurroundOutput(); sendEnabledSurroundFormats(mContentResolver, mSurroundModeChanged); updateAssistantUIdLocked(/* forceUpdate= */ false); + + boolean linkNotificationWithVolume = Settings.Secure.getInt(mContentResolver, + Settings.Secure.VOLUME_LINK_NOTIFICATION, 1) == 1; + if (linkNotificationWithVolume != mLinkNotificationWithVolume) { + mLinkNotificationWithVolume = linkNotificationWithVolume; + createStreamStates(); + updateStreamVolumeAlias(true, TAG); + } } } diff --git a/services/core/java/com/android/server/audio/MediaFocusControl.java b/services/core/java/com/android/server/audio/MediaFocusControl.java index 69a4c23cf867..0e516f0ae6c7 100644 --- a/services/core/java/com/android/server/audio/MediaFocusControl.java +++ b/services/core/java/com/android/server/audio/MediaFocusControl.java @@ -21,6 +21,7 @@ import android.app.AppOpsManager; import android.content.ContentResolver; import android.content.Context; +import android.database.ContentObserver; import android.media.AudioAttributes; import android.media.AudioFocusInfo; import android.media.AudioManager; @@ -101,13 +102,15 @@ public class MediaFocusControl implements PlayerFocusEnforcer { @GuardedBy("mExtFocusChangeLock") private long mExtFocusChangeCounter; + // Observer to work with per-app volume + private SettingsObserver mSettingsObserver; + protected MediaFocusControl(Context cntxt, PlayerFocusEnforcer pfe) { mContext = cntxt; mAppOps = (AppOpsManager)mContext.getSystemService(Context.APP_OPS_SERVICE); mFocusEnforcer = pfe; final ContentResolver cr = mContext.getContentResolver(); - mMultiAudioFocusEnabled = Settings.System.getIntForUser(cr, - Settings.System.MULTI_AUDIO_FOCUS_ENABLED, 0, cr.getUserId()) != 0; + mSettingsObserver = new SettingsObserver(); initFocusThreading(); } @@ -1355,4 +1358,23 @@ public int hashCode() { return mUid; } } + + private class SettingsObserver extends ContentObserver { + + SettingsObserver() { + super(new Handler()); + ContentResolver cr = mContext.getContentResolver(); + cr.registerContentObserver(Settings.System.getUriFor( + Settings.System.SHOW_APP_VOLUME), false, this); + } + + @Override + public void onChange(boolean selfChange) { + super.onChange(selfChange); + ContentResolver cr = mContext.getContentResolver(); + mMultiAudioFocusEnabled = Settings.System.getIntForUser(cr, + Settings.System.SHOW_APP_VOLUME, 0, cr.getUserId()) != 0; + updateMultiAudioFocus(mMultiAudioFocusEnabled); + } + } } diff --git a/services/core/java/com/android/server/biometrics/AuthService.java b/services/core/java/com/android/server/biometrics/AuthService.java index bc550d311370..fc537c639d1d 100644 --- a/services/core/java/com/android/server/biometrics/AuthService.java +++ b/services/core/java/com/android/server/biometrics/AuthService.java @@ -31,6 +31,8 @@ import static android.hardware.biometrics.BiometricConstants.BIOMETRIC_ERROR_CANCELED; import static android.hardware.biometrics.BiometricManager.Authenticators; +import static com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintProvider.getWorkaroundSensorProps; + import android.annotation.NonNull; import android.annotation.Nullable; import android.app.AppOpsManager; @@ -780,6 +782,10 @@ private FingerprintSensorPropertiesInternal getHidlFingerprintSensorProps(int se final int[] udfpsProps = getContext().getResources().getIntArray( com.android.internal.R.array.config_udfps_sensor_props); + // Non-empty workaroundLocations indicates that the sensor is SFPS. + final List workaroundLocations = + getWorkaroundSensorProps(getContext()); + final boolean isUdfps = !ArrayUtils.isEmpty(udfpsProps); // config_is_powerbutton_fps indicates whether device has a power button fingerprint sensor. @@ -809,6 +815,11 @@ private FingerprintSensorPropertiesInternal getHidlFingerprintSensorProps(int se resetLockoutRequiresHardwareAuthToken, List.of(new SensorLocationInternal("" /* display */, udfpsProps[0], udfpsProps[1], udfpsProps[2]))); + } else if (!workaroundLocations.isEmpty()) { + return new FingerprintSensorPropertiesInternal(sensorId, + Utils.authenticatorStrengthToPropertyStrength(strength), maxEnrollmentsPerUser, + componentInfo, sensorType, true /* halControlsIllumination */, + resetLockoutRequiresHardwareAuthToken, workaroundLocations); } else { return new FingerprintSensorPropertiesInternal(sensorId, Utils.authenticatorStrengthToPropertyStrength(strength), maxEnrollmentsPerUser, diff --git a/services/core/java/com/android/server/biometrics/AuthSession.java b/services/core/java/com/android/server/biometrics/AuthSession.java index 41ca13f5d5f5..2af0af945d3c 100644 --- a/services/core/java/com/android/server/biometrics/AuthSession.java +++ b/services/core/java/com/android/server/biometrics/AuthSession.java @@ -70,6 +70,8 @@ import java.util.Random; import java.util.function.Function; +import com.android.internal.util.custom.faceunlock.FaceUnlockUtils; + /** * Class that defines the states of an authentication session invoked via * {@link android.hardware.biometrics.BiometricPrompt}, as well as all of the necessary @@ -331,6 +333,9 @@ void onCookieReceived(int cookie) { } private boolean isConfirmationRequired(BiometricSensor sensor) { + if (sensor.modality == TYPE_FACE && FaceUnlockUtils.isFaceUnlockSupported()) { + return true; + } return sensor.confirmationSupported() && (sensor.confirmationAlwaysRequired(mUserId) || mPreAuthInfo.confirmationRequested); diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java index 4767969bd3ed..4805d97c34cd 100644 --- a/services/core/java/com/android/server/biometrics/BiometricService.java +++ b/services/core/java/com/android/server/biometrics/BiometricService.java @@ -85,6 +85,8 @@ import java.util.concurrent.atomic.AtomicLong; import java.util.function.Supplier; +import com.android.internal.util.custom.faceunlock.FaceUnlockUtils; + /** * System service that arbitrates the modality for BiometricPrompt to use. */ @@ -214,10 +216,6 @@ public static class SettingObserver extends ContentObserver { private static final boolean DEFAULT_APP_ENABLED = true; private static final boolean DEFAULT_ALWAYS_REQUIRE_CONFIRMATION = false; - // Some devices that shipped before S already have face-specific settings. Instead of - // migrating, which is complicated, let's just keep using the existing settings. - private final boolean mUseLegacyFaceOnlySettings; - // Only used for legacy face-only devices private final Uri FACE_UNLOCK_KEYGUARD_ENABLED = Settings.Secure.getUriFor(Settings.Secure.FACE_UNLOCK_KEYGUARD_ENABLED); @@ -257,18 +255,13 @@ public SettingObserver(Context context, Handler handler, final boolean hasFace = context.getPackageManager() .hasSystemFeature(PackageManager.FEATURE_FACE); - // Use the legacy setting on face-only devices that shipped on or before Q - mUseLegacyFaceOnlySettings = - Build.VERSION.DEVICE_INITIAL_SDK_INT <= Build.VERSION_CODES.Q - && hasFace && !hasFingerprint; - updateContentObserver(); } public void updateContentObserver() { mContentResolver.unregisterContentObserver(this); - if (mUseLegacyFaceOnlySettings) { + if (FaceUnlockUtils.isFaceUnlockSupported()) { mContentResolver.registerContentObserver(FACE_UNLOCK_KEYGUARD_ENABLED, false /* notifyForDescendants */, this /* observer */, @@ -338,7 +331,7 @@ public void onChange(boolean selfChange, Uri uri, int userId) { public boolean getEnabledOnKeyguard(int userId) { if (!mBiometricEnabledOnKeyguard.containsKey(userId)) { - if (mUseLegacyFaceOnlySettings) { + if (FaceUnlockUtils.isFaceUnlockSupported()) { onChange(true /* selfChange */, FACE_UNLOCK_KEYGUARD_ENABLED, userId); } else { onChange(true /* selfChange */, BIOMETRIC_KEYGUARD_ENABLED, userId); @@ -349,7 +342,7 @@ public boolean getEnabledOnKeyguard(int userId) { public boolean getEnabledForApps(int userId) { if (!mBiometricEnabledForApps.containsKey(userId)) { - if (mUseLegacyFaceOnlySettings) { + if (FaceUnlockUtils.isFaceUnlockSupported()) { onChange(true /* selfChange */, FACE_UNLOCK_APP_ENABLED, userId); } else { onChange(true /* selfChange */, BIOMETRIC_APP_ENABLED, userId); @@ -1323,9 +1316,6 @@ private BiometricSensor getSensorForId(int sensorId) { } private void dumpInternal(PrintWriter pw) { - pw.println("Legacy Settings: " + mSettingObserver.mUseLegacyFaceOnlySettings); - pw.println(); - pw.println("Sensors:"); for (BiometricSensor sensor : mSensors) { pw.println(" " + sensor); diff --git a/services/core/java/com/android/server/biometrics/PreAuthInfo.java b/services/core/java/com/android/server/biometrics/PreAuthInfo.java index aec98f0ea426..b694746b69b7 100644 --- a/services/core/java/com/android/server/biometrics/PreAuthInfo.java +++ b/services/core/java/com/android/server/biometrics/PreAuthInfo.java @@ -42,6 +42,8 @@ import java.util.ArrayList; import java.util.List; +import com.android.internal.util.custom.faceunlock.FaceUnlockUtils; + /** * Class representing the calling client's request. Additionally, derives/calculates * preliminary info that would be useful in helping serve this request. Note that generating @@ -227,6 +229,9 @@ static PreAuthInfo create(ITrustManager trustManager, private static boolean isEnabledForApp(BiometricService.SettingObserver settingObserver, @BiometricAuthenticator.Modality int modality, int userId) { + if (modality == TYPE_FINGERPRINT && FaceUnlockUtils.isFaceUnlockSupported()){ + return true; + } return settingObserver.getEnabledForApps(userId); } diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java index 63609f77dc75..6b2030087fa9 100644 --- a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java +++ b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java @@ -60,6 +60,9 @@ public class BiometricScheduler { private static final String BASE_TAG = "BiometricScheduler"; + + private boolean mCancel; + // Number of recent operations to keep in our logs for dumpsys protected static final int LOG_NUM_RECENT_OPERATIONS = 50; @@ -238,13 +241,16 @@ public void onClientFinished(@NonNull BaseClientMonitor clientMonitor, boolean s * @param gestureAvailabilityDispatcher may be null if the sensor does not support gestures * (such as fingerprint swipe). */ - public BiometricScheduler(@NonNull String tag, + public BiometricScheduler(Context context,@NonNull String tag, @SensorType int sensorType, @Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher) { this(tag, new Handler(Looper.getMainLooper()), sensorType, gestureAvailabilityDispatcher, IBiometricService.Stub.asInterface( ServiceManager.getService(Context.BIOMETRIC_SERVICE)), LOG_NUM_RECENT_OPERATIONS, CoexCoordinator.getInstance()); + + mCancel = context.getResources().getBoolean( + com.android.internal.R.bool.config_fpCancelIfNotIdle); } @VisibleForTesting @@ -258,8 +264,13 @@ protected String getTag() { protected void startNextOperationIfIdle() { if (mCurrentOperation != null) { - Slog.v(getTag(), "Not idle, current operation: " + mCurrentOperation); - return; + if (mCancel && !mCurrentOperation.isFinished()) { + Slog.v(getTag(), "Not idle, cancelling current operation: " + mCurrentOperation); + mCurrentOperation.cancel(mHandler, mInternalCallback); + } else { + Slog.v(getTag(), "Not idle, current operation: " + mCurrentOperation); + return; + } } if (mPendingOperations.isEmpty()) { Slog.d(getTag(), "No operations, returning to idle"); diff --git a/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java b/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java index 7f8f38f3e9d2..5097b6ec9880 100644 --- a/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java @@ -45,6 +45,8 @@ public abstract class InternalEnumerateClient extends HalClientMonitor // List of templates to remove from the HAL private List mUnknownHALTemplates = new ArrayList<>(); + private boolean mCleanup; + protected InternalEnumerateClient(@NonNull Context context, @NonNull Supplier lazyDaemon, @NonNull IBinder token, int userId, @NonNull String owner, @NonNull List enrolledList, @@ -58,6 +60,8 @@ protected InternalEnumerateClient(@NonNull Context context, @NonNull Supplier // BiometricsProtoEnums.CLIENT_UNKNOWN); mEnrolledList = enrolledList; mUtils = utils; + mCleanup = context.getResources().getBoolean( + com.android.internal.R.bool.config_cleanupUnusedFingerprints); } @Override @@ -114,12 +118,13 @@ private void doTemplateCleanup() { // At this point, mEnrolledList only contains templates known to the framework and // not the HAL. for (int i = 0; i < mEnrolledList.size(); i++) { - BiometricAuthenticator.Identifier identifier = mEnrolledList.get(i); - Slog.e(TAG, "doTemplateCleanup(): Removing dangling template from framework: " - + identifier.getBiometricId() + " " + identifier.getName()); - mUtils.removeBiometricForUser(getContext(), - getTargetUserId(), identifier.getBiometricId()); - + if (mCleanup) { + BiometricAuthenticator.Identifier identifier = mEnrolledList.get(i); + Slog.e(TAG, "doTemplateCleanup(): Removing dangling template from framework: " + + identifier.getBiometricId() + " " + identifier.getName()); + mUtils.removeBiometricForUser(getContext(), + getTargetUserId(), identifier.getBiometricId()); + } getLogger().logUnknownEnrollmentInFramework(); } mEnrolledList.clear(); diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java index 2e820574b435..ccbd10c8e8e3 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java @@ -25,6 +25,7 @@ import android.annotation.Nullable; import android.content.Context; import android.hardware.biometrics.BiometricManager; +import android.hardware.biometrics.SensorProperties; import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.IBiometricSensorReceiver; import android.hardware.biometrics.IBiometricService; @@ -62,6 +63,7 @@ import com.android.server.biometrics.sensors.LockoutResetDispatcher; import com.android.server.biometrics.sensors.LockoutTracker; import com.android.server.biometrics.sensors.face.aidl.FaceProvider; +import com.android.server.biometrics.sensors.face.custom.CustomFaceProvider; import com.android.server.biometrics.sensors.face.hidl.Face10; import java.io.FileDescriptor; @@ -654,6 +656,12 @@ private void addAidlProviders() { } } + private void addCustomProviders() { + if (CustomFaceProvider.useCustomFaceUnlockService()) { + mServiceProviders.add(new CustomFaceProvider(getContext(), new FaceSensorPropertiesInternal(CustomFaceProvider.DEVICE_ID, SensorProperties.STRENGTH_WEAK, 1, new ArrayList(), 1, false, false, false), mLockoutResetDispatcher)); + } + } + @Override // Binder call public void registerAuthenticators( @NonNull List hidlSensors) { @@ -671,6 +679,7 @@ public void registerAuthenticators( handler.post(() -> { addHidlProviders(hidlSensors); addAidlProviders(); + addCustomProviders(); final IBiometricService biometricService = IBiometricService.Stub.asInterface( ServiceManager.getService(Context.BIOMETRIC_SERVICE)); diff --git a/services/core/java/com/android/server/biometrics/sensors/face/custom/ArrayUtils.java b/services/core/java/com/android/server/biometrics/sensors/face/custom/ArrayUtils.java new file mode 100644 index 000000000000..617781c0a08f --- /dev/null +++ b/services/core/java/com/android/server/biometrics/sensors/face/custom/ArrayUtils.java @@ -0,0 +1,65 @@ +/* +* Copyright (C) 2022 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.server.biometrics.sensors.face.custom; + +import java.util.ArrayList; + +public class ArrayUtils { + public static ArrayList toByteArrayList(byte[] in) { + if (in == null) { + return null; + } + ArrayList out = new ArrayList<>(in.length); + for (byte c : in) { + out.add(c); + } + return out; + } + + public static ArrayList toIntArrayList(int[] in) { + if (in == null) { + return null; + } + ArrayList out = new ArrayList<>(in.length); + for (int c : in) { + out.add(c); + } + return out; + } + + public static int[] toIntArray(ArrayList in) { + if (in == null) { + return null; + } + int[] out = new int[in.size()]; + for (int i = 0; i < in.size(); i++) { + out[i] = in.get(i); + } + return out; + } + + public static byte[] toByteArray(ArrayList in) { + if (in == null) { + return null; + } + byte[] out = new byte[in.size()]; + for (int i = 0; i < in.size(); i++) { + out[i] = in.get(i); + } + return out; + } +} diff --git a/services/core/java/com/android/server/biometrics/sensors/face/custom/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/face/custom/BiometricTestSessionImpl.java new file mode 100644 index 000000000000..83e772b0e20b --- /dev/null +++ b/services/core/java/com/android/server/biometrics/sensors/face/custom/BiometricTestSessionImpl.java @@ -0,0 +1,165 @@ +/* +* Copyright (C) 2022 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.server.biometrics.sensors.face.custom; + +import android.content.Context; +import android.hardware.biometrics.ITestSession; +import android.hardware.biometrics.ITestSessionCallback; +import android.hardware.face.Face; +import android.hardware.face.FaceAuthenticationFrame; +import android.hardware.face.FaceEnrollFrame; +import android.hardware.face.IFaceServiceReceiver; +import android.os.Binder; +import android.os.RemoteException; +import android.util.Slog; + +import com.android.server.biometrics.Utils; +import com.android.server.biometrics.sensors.BaseClientMonitor; +import com.android.server.biometrics.sensors.ClientMonitorCallback; +import com.android.server.biometrics.sensors.face.FaceUtils; + +import java.util.List; +import java.util.Random; + +public class BiometricTestSessionImpl extends ITestSession.Stub { + private static final String TAG = "BiometricTestSessionImpl"; + private final ITestSessionCallback mCallback; + private final Context mContext; + private final CustomFaceProvider.HalResultController mHalResultController; + private final CustomFaceProvider mCustomFaceProvider; + private final int mSensorId; + private final IFaceServiceReceiver mReceiver = new IFaceServiceReceiver.Stub() { + @Override + public void onEnrollResult(Face face, int remaining) { + } + + @Override + public void onAcquired(int acquiredInfo, int vendorCode) { + } + + @Override + public void onAuthenticationSucceeded(Face face, int userId, boolean isStrongBiometric) { + } + + @Override + public void onFaceDetected(int sensorId, int userId, boolean isStrongBiometric) { + } + + @Override + public void onAuthenticationFailed() { + } + + @Override + public void onError(int error, int vendorCode) { + } + + @Override + public void onRemoved(Face face, int remaining) { + } + + @Override + public void onFeatureSet(boolean success, int feature) { + } + + @Override + public void onFeatureGet(boolean success, int[] features, boolean[] featureState) { + } + + public void onChallengeGenerated(int sensorId, int userId, long challenge) { + } + + @Override + public void onAuthenticationFrame(FaceAuthenticationFrame frame) { + } + + @Override + public void onEnrollmentFrame(FaceEnrollFrame frame) { + } + }; + private final Random mRandom = new Random(); + + public BiometricTestSessionImpl(Context context, int sensorId, ITestSessionCallback callback, CustomFaceProvider customFaceProvider, CustomFaceProvider.HalResultController halResultController) { + mContext = context; + mSensorId = sensorId; + mCallback = callback; + mCustomFaceProvider = customFaceProvider; + mHalResultController = halResultController; + } + + public void setTestHalEnabled(boolean enabled) { + Utils.checkPermission(mContext, "android.permission.TEST_BIOMETRIC"); + mCustomFaceProvider.setTestHalEnabled(enabled); + } + + public void startEnroll(int userId) { + Utils.checkPermission(mContext, "android.permission.TEST_BIOMETRIC"); + mCustomFaceProvider.scheduleEnroll(mSensorId, new Binder(), new byte[69], userId, mReceiver, mContext.getOpPackageName(), new int[0], null, false); + } + + public void finishEnroll(int userId) { + Utils.checkPermission(mContext, "android.permission.TEST_BIOMETRIC"); + mHalResultController.onEnrollResult(1, userId, 0); + } + + public void acceptAuthentication(int userId) { + Utils.checkPermission(mContext, "android.permission.TEST_BIOMETRIC"); + List faces = FaceUtils.getLegacyInstance(mSensorId).getBiometricsForUser(mContext, userId); + if (faces.isEmpty()) { + Slog.w(TAG, "No faces, returning"); + } else { + mHalResultController.onAuthenticated(faces.get(0).getBiometricId(), userId, new byte[]{0}); + } + } + + public void rejectAuthentication(int userId) { + Utils.checkPermission(mContext, "android.permission.TEST_BIOMETRIC"); + mHalResultController.onAuthenticated(0, userId, null); + } + + public void notifyAcquired(int userId, int acquireInfo) { + Utils.checkPermission(mContext, "android.permission.TEST_BIOMETRIC"); + mHalResultController.onAcquired(userId, acquireInfo, 0); + } + + public void notifyError(int userId, int errorCode) { + Utils.checkPermission(mContext, "android.permission.TEST_BIOMETRIC"); + mHalResultController.onError(errorCode, 0); + } + + public void cleanupInternalState(int userId) { + Utils.checkPermission(mContext, "android.permission.TEST_BIOMETRIC"); + mCustomFaceProvider.scheduleInternalCleanup(mSensorId, userId, new ClientMonitorCallback() { + @Override + public void onClientStarted(BaseClientMonitor clientMonitor) { + try { + mCallback.onCleanupStarted(clientMonitor.getTargetUserId()); + } catch (RemoteException e) { + Slog.e(BiometricTestSessionImpl.TAG, "Remote exception", e); + } + } + + @Override + public void onClientFinished(BaseClientMonitor clientMonitor, boolean success) { + try { + mCallback.onCleanupFinished(clientMonitor.getTargetUserId()); + } catch (RemoteException e) { + Slog.e(BiometricTestSessionImpl.TAG, "Remote exception", e); + } + } + }); + } +} diff --git a/services/core/java/com/android/server/biometrics/sensors/face/custom/CustomFaceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/custom/CustomFaceProvider.java new file mode 100644 index 000000000000..0b0119970c06 --- /dev/null +++ b/services/core/java/com/android/server/biometrics/sensors/face/custom/CustomFaceProvider.java @@ -0,0 +1,848 @@ +/* +* Copyright (C) 2022 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.server.biometrics.sensors.face.custom; + +import android.app.ActivityManager; +import android.app.SynchronousUserSwitchObserver; +import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.ServiceConnection; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.content.pm.UserInfo; +import android.hardware.biometrics.BiometricsProtoEnums; +import android.hardware.biometrics.IInvalidationCallback; +import android.hardware.biometrics.ITestSession; +import android.hardware.biometrics.ITestSessionCallback; +import android.hardware.face.Face; +import android.hardware.face.FaceSensorPropertiesInternal; +import android.hardware.face.IFaceServiceReceiver; +import android.os.Binder; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.RemoteException; +import android.os.UserHandle; +import android.os.UserManager; +import android.provider.Settings; +import android.util.Slog; +import android.util.SparseArray; +import android.util.proto.ProtoOutputStream; +import android.view.Surface; + +import android.annotation.NonNull; + +import com.android.internal.util.custom.faceunlock.FaceUnlockUtils; +import com.android.internal.util.custom.faceunlock.IFaceService; +import com.android.server.biometrics.SensorServiceStateProto; +import com.android.server.biometrics.SensorStateProto; +import com.android.server.biometrics.UserStateProto; +import com.android.server.biometrics.Utils; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; +import com.android.server.biometrics.sensors.AcquisitionClient; +import com.android.server.biometrics.sensors.AuthenticationConsumer; +import com.android.server.biometrics.sensors.BaseClientMonitor; +import com.android.server.biometrics.sensors.ClientMonitorCallback; +import com.android.server.biometrics.sensors.BiometricNotificationUtils; +import com.android.server.biometrics.sensors.BiometricScheduler; +import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; +import com.android.server.biometrics.sensors.EnumerateConsumer; +import com.android.server.biometrics.sensors.ErrorConsumer; +import com.android.server.biometrics.sensors.LockoutResetDispatcher; +import com.android.server.biometrics.sensors.PerformanceTracker; +import com.android.server.biometrics.sensors.RemovalConsumer; +import com.android.server.biometrics.sensors.face.FaceUtils; +import com.android.server.biometrics.sensors.face.LockoutHalImpl; +import com.android.server.biometrics.sensors.face.ServiceProvider; +import com.android.server.biometrics.sensors.face.UsageStats; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.time.Clock; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicLong; +import java.util.function.Supplier; + +public class CustomFaceProvider implements ServiceProvider { + public static final int DEVICE_ID = 1008; + private static final int ENROLL_TIMEOUT_SEC = 75; + private static final int GENERATE_CHALLENGE_COUNTER_TTL_MILLIS = 600000; + private static final int GENERATE_CHALLENGE_REUSE_INTERVAL_MILLIS = 60000; + private static final String TAG = CustomFaceProvider.class.getSimpleName(); + public static Clock sSystemClock = Clock.systemUTC(); + final SparseArray mFaceServices; + private final Map mAuthenticatorIds; + private final Context mContext; + private final List mGeneratedChallengeCount; + private final HalResultController mHalResultController; + private final Handler mHandler; + private final Supplier mLazyDaemon; + private final LockoutHalImpl mLockoutTracker; + private final BiometricScheduler mScheduler; + private final int mSensorId; + private final FaceSensorPropertiesInternal mSensorProperties; + private final UsageStats mUsageStats; + @NonNull + private final AtomicLong mRequestCounter = new AtomicLong(0); + private int mCurrentUserId; + private FaceGenerateChallengeClient mGeneratedChallengeCache; + private boolean mIsServiceBinding; + private TestHal mTestHal; + private boolean mTestHalEnabled; + + private BiometricContext mBiometricContext; + + CustomFaceProvider(Context context, FaceSensorPropertiesInternal sensorProps, LockoutResetDispatcher lockoutResetDispatcher, BiometricScheduler scheduler) { + mBiometricContext = BiometricContext.getInstance(context); + mTestHalEnabled = false; + mCurrentUserId = -10000; + mGeneratedChallengeCount = new ArrayList<>(); + mGeneratedChallengeCache = null; + mFaceServices = new SparseArray<>(); + mIsServiceBinding = false; + mSensorProperties = sensorProps; + mContext = context; + mSensorId = sensorProps.sensorId; + mScheduler = scheduler; + Handler handler = new Handler(Looper.getMainLooper()); + mHandler = handler; + mUsageStats = new UsageStats(context); + mAuthenticatorIds = new HashMap<>(); + mLazyDaemon = CustomFaceProvider.this::getDaemon; + LockoutHalImpl lockoutHalImpl = new LockoutHalImpl(); + mLockoutTracker = lockoutHalImpl; + HalResultController halResultController = new HalResultController(sensorProps.sensorId, context, handler, scheduler, lockoutHalImpl, lockoutResetDispatcher); + mHalResultController = halResultController; + halResultController.setCallback(() -> { + mCurrentUserId = -10000; + }); + mCurrentUserId = ActivityManager.getCurrentUser(); + try { + ActivityManager.getService().registerUserSwitchObserver(new SynchronousUserSwitchObserver() { + public void onUserSwitching(int newUserId) { + Slog.d(TAG, "user switch : newUserId = " + newUserId); + mCurrentUserId = newUserId; + if (getDaemon() == null) { + bindFaceAuthService(mCurrentUserId); + } + } + }, TAG); + } catch (RemoteException e) { + Slog.e(TAG, "Unable to register user switch observer"); + } + context.registerReceiver(new BroadcastReceiver() { + @Override + public void onReceive(Context context2, Intent intent) { + if (getDaemon() == null) { + bindFaceAuthService(mCurrentUserId); + } + } + }, new IntentFilter("android.intent.action.USER_UNLOCKED")); + } + + public CustomFaceProvider(Context context, FaceSensorPropertiesInternal sensorProps, LockoutResetDispatcher lockoutResetDispatcher) { + this(context, sensorProps, lockoutResetDispatcher, new BiometricScheduler(context, TAG, 0, null)); + } + + synchronized IFaceService getDaemon() { + if (mTestHalEnabled) { + if (mTestHal == null) { + mTestHal = new TestHal(mCurrentUserId, mContext, mSensorId); + } + try { + mTestHal.setCallback(mHalResultController); + } catch (RemoteException e) { + e.printStackTrace(); + } + return mTestHal; + } + IFaceService service = getFaceService(mCurrentUserId); + if (service == null) { + bindFaceAuthService(mCurrentUserId); + } + return service; + } + + @Override + public boolean containsSensor(int sensorId) { + return mSensorId == sensorId; + } + + @Override + public List getSensorProperties() { + List properties = new ArrayList<>(); + properties.add(mSensorProperties); + return properties; + } + + @Override + public FaceSensorPropertiesInternal getSensorProperties(int sensorId) { + return mSensorProperties; + } + + @Override + public List getEnrolledFaces(int sensorId, int userId) { + return FaceUtils.getLegacyInstance(mSensorId).getBiometricsForUser(mContext, userId); + } + + @Override + public int getLockoutModeForUser(int sensorId, int userId) { + return mLockoutTracker.getLockoutModeForUser(userId); + } + + @Override + public long getAuthenticatorId(int sensorId, int userId) { + return mAuthenticatorIds.getOrDefault(Integer.valueOf(userId), 0L).longValue(); + } + + @Override + public boolean isHardwareDetected(int sensorId) { + return getDaemon() != null; + } + + private boolean isGeneratedChallengeCacheValid() { + return mGeneratedChallengeCache != null && sSystemClock.millis() - mGeneratedChallengeCache.getCreatedAt() < GENERATE_CHALLENGE_REUSE_INTERVAL_MILLIS; + } + + private void incrementChallengeCount() { + mGeneratedChallengeCount.add(0, sSystemClock.millis()); + } + + private int decrementChallengeCount() { + mGeneratedChallengeCount.removeIf(aLong -> sSystemClock.millis() - aLong > GENERATE_CHALLENGE_COUNTER_TTL_MILLIS); + if (!mGeneratedChallengeCount.isEmpty()) { + mGeneratedChallengeCount.remove(0); + } + return mGeneratedChallengeCount.size(); + } + + @Override + public void scheduleGenerateChallenge(int sensorId, int userId, IBinder token, IFaceServiceReceiver receiver, String opPackageName) { + mHandler.post(() -> { + if (getDaemon() == null) { + bindFaceAuthService(mCurrentUserId); + try { + receiver.onChallengeGenerated(sensorId, userId, 0); + } catch (RemoteException e) { + e.printStackTrace(); + } + } else { + incrementChallengeCount(); + if (isGeneratedChallengeCacheValid()) { + Slog.d(TAG, "Current challenge is cached and will be reused"); + mGeneratedChallengeCache.reuseResult(receiver); + return; + } + scheduleUpdateActiveUserWithoutHandler(userId); + final FaceGenerateChallengeClient client = new FaceGenerateChallengeClient(mContext, mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), userId, opPackageName, mSensorId, createLogger(BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN), mBiometricContext, sSystemClock.millis()); + mGeneratedChallengeCache = client; + mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() { + @Override + public void onClientStarted(BaseClientMonitor clientMonitor) { + if (client != clientMonitor) { + Slog.e(TAG, "scheduleGenerateChallenge onClientStarted, mismatched client. Expecting: " + client + ", received: " + clientMonitor); + } + } + }); + } + }); + } + + @Override + public void scheduleRevokeChallenge(int sensorId, int userId, IBinder token, String opPackageName, long challenge) { + mHandler.post(() -> { + if (getDaemon() == null) { + bindFaceAuthService(mCurrentUserId); + return; + } + if (!(decrementChallengeCount() == 0)) { + Slog.w(TAG, "scheduleRevokeChallenge skipped - challenge still in use: " + mGeneratedChallengeCount); + return; + } + Slog.d(TAG, "scheduleRevokeChallenge executing - no active clients"); + mGeneratedChallengeCache = null; + final FaceRevokeChallengeClient client = new FaceRevokeChallengeClient(mContext, mLazyDaemon, token, userId, opPackageName, mSensorId, createLogger(BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN), mBiometricContext); + mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() { + @Override + public void onClientFinished(BaseClientMonitor clientMonitor, boolean success) { + if (client != clientMonitor) { + Slog.e(TAG, "scheduleRevokeChallenge, mismatched client.Expecting: " + client + ", received: " + clientMonitor); + } + } + }); + }); + } + + @Override + public long scheduleEnroll(int sensorId, IBinder token, byte[] hardwareAuthToken, int userId, IFaceServiceReceiver receiver, String opPackageName, int[] disabledFeatures, Surface previewSurface, boolean debugConsent) { + final long id = mRequestCounter.incrementAndGet(); + mHandler.post(() -> { + if (getDaemon() == null) { + bindFaceAuthService(mCurrentUserId); + try { + receiver.onError(2, 0); + } catch (RemoteException e) { + e.printStackTrace(); + } + } else { + scheduleUpdateActiveUserWithoutHandler(userId); + BiometricNotificationUtils.cancelReEnrollNotification(mContext); + final FaceEnrollClient client = new FaceEnrollClient(mContext, mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), userId, hardwareAuthToken, opPackageName, FaceUtils.getLegacyInstance(mSensorId), disabledFeatures, ENROLL_TIMEOUT_SEC, previewSurface, mSensorId, createLogger(BiometricsProtoEnums.ACTION_ENROLL, BiometricsProtoEnums.CLIENT_UNKNOWN), mBiometricContext); + mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() { + @Override + public void onClientFinished(BaseClientMonitor clientMonitor, boolean success) { + if (success) { + scheduleUpdateActiveUserWithoutHandler(client.getTargetUserId()); + } + } + }); + } + }); + return id; + } + + @Override + public void cancelEnrollment(int sensorId, IBinder token, long requestId) { + mHandler.post(() -> mScheduler.cancelEnrollment(token, requestId)); + } + + @Override + public long scheduleFaceDetect(int sensorId, IBinder token, int userId, ClientMonitorCallbackConverter callback, String opPackageName, int statsClient) { + throw new IllegalStateException("Face detect not supported by IBiometricsFace@1.0. Did youforget to check the supportsFaceDetection flag?"); + } + + @Override + public void cancelFaceDetect(int sensorId, IBinder token, long requestId) { + throw new IllegalStateException("Face detect not supported by IBiometricsFace@1.0. Did youforget to check the supportsFaceDetection flag?"); + } + + @Override + public long scheduleAuthenticate(int sensorId, IBinder token, long operationId, + int userId, int cookie, ClientMonitorCallbackConverter receiver, + String opPackageName, boolean restricted, int statsClient, + boolean allowBackgroundAuthentication, boolean isKeyguardBypassEnabled) { + final long id = mRequestCounter.incrementAndGet(); + scheduleAuthenticate(sensorId, token, operationId, userId, cookie, receiver, + opPackageName, id, restricted, statsClient, + allowBackgroundAuthentication, isKeyguardBypassEnabled); + return id; + } + + @Override + public void scheduleAuthenticate(int sensorId, IBinder token, long operationId, int userId, int cookie, ClientMonitorCallbackConverter receiver, String opPackageName, long requestId, boolean restricted, int statsClient, boolean allowBackgroundAuthentication, boolean isKeyguardBypassEnabled) { + mHandler.post(() -> { + if (getDaemon() == null) { + bindFaceAuthService(mCurrentUserId); + try { + receiver.onError(DEVICE_ID, 0, 1, 0); + } catch (RemoteException e) { + e.printStackTrace(); + } + } else { + scheduleUpdateActiveUserWithoutHandler(userId); + mScheduler.scheduleClientMonitor(new FaceAuthenticationClient(mContext, mLazyDaemon, token, requestId, receiver, userId, operationId, restricted, opPackageName, cookie, false, mSensorId, createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient), mBiometricContext, Utils.isStrongBiometric(mSensorId), mLockoutTracker, mUsageStats, allowBackgroundAuthentication)); + } + }); + } + + @Override + public void cancelAuthentication(int sensorId, IBinder token, long requestId) { + mHandler.post(() -> mScheduler.cancelAuthenticationOrDetection(token, requestId)); + } + + @Override + public void scheduleRemove(int sensorId, IBinder token, int faceId, int userId, IFaceServiceReceiver receiver, String opPackageName) { + mHandler.post(() -> { + if (getDaemon() == null) { + bindFaceAuthService(mCurrentUserId); + try { + receiver.onError(1, 0); + } catch (RemoteException e) { + e.printStackTrace(); + } + } else { + scheduleUpdateActiveUserWithoutHandler(userId); + mScheduler.scheduleClientMonitor(new FaceRemovalClient(mContext, mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), faceId, userId, opPackageName, FaceUtils.getLegacyInstance(mSensorId), mSensorId, createLogger(BiometricsProtoEnums.ACTION_REMOVE, BiometricsProtoEnums.CLIENT_UNKNOWN), mBiometricContext, mAuthenticatorIds)); + } + }); + } + + @Override + public void scheduleRemoveAll(int sensorId, IBinder token, int userId, IFaceServiceReceiver receiver, String opPackageName) { + mHandler.post(() -> { + if (getDaemon() == null) { + bindFaceAuthService(mCurrentUserId); + try { + receiver.onError(1, 0); + } catch (RemoteException e) { + e.printStackTrace(); + } + } else { + scheduleUpdateActiveUserWithoutHandler(userId); + mScheduler.scheduleClientMonitor(new FaceRemovalClient(mContext, mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), 0, userId, opPackageName, FaceUtils.getLegacyInstance(mSensorId), mSensorId, createLogger(BiometricsProtoEnums.ACTION_REMOVE, BiometricsProtoEnums.CLIENT_UNKNOWN), mBiometricContext, mAuthenticatorIds)); + } + }); + } + + @Override + public void scheduleResetLockout(int sensorId, int userId, byte[] hardwareAuthToken) { + mHandler.post(() -> { + if (getDaemon() == null) { + bindFaceAuthService(mCurrentUserId); + } else if (getEnrolledFaces(sensorId, userId).isEmpty()) { + Slog.w(TAG, "Ignoring lockout reset, no templates enrolled for user: " + userId); + } else { + scheduleUpdateActiveUserWithoutHandler(userId); + mScheduler.scheduleClientMonitor(new FaceResetLockoutClient(mContext, mLazyDaemon, userId, mContext.getOpPackageName(), mSensorId, createLogger(BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN), mBiometricContext, hardwareAuthToken)); + } + }); + } + + @Override + public void scheduleSetFeature(int sensorId, IBinder token, int userId, int feature, boolean enabled, byte[] hardwareAuthToken, IFaceServiceReceiver receiver, String opPackageName) { + mHandler.post(() -> { + if (getDaemon() == null) { + bindFaceAuthService(mCurrentUserId); + return; + } + List faces = getEnrolledFaces(sensorId, userId); + if (faces.isEmpty()) { + Slog.w(TAG, "Ignoring setFeature, no templates enrolled for user: " + userId); + return; + } + scheduleUpdateActiveUserWithoutHandler(userId); + mScheduler.scheduleClientMonitor(new FaceSetFeatureClient(mContext, mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), userId, opPackageName, mSensorId, BiometricLogger.ofUnknown(mContext), mBiometricContext, feature, enabled, hardwareAuthToken, faces.get(0).getBiometricId())); + }); + } + + @Override + public void scheduleGetFeature(int sensorId, IBinder token, int userId, int feature, ClientMonitorCallbackConverter listener, String opPackageName) { + mHandler.post(() -> { + if (getDaemon() == null) { + bindFaceAuthService(mCurrentUserId); + if (listener != null) { + try { + listener.onError(DEVICE_ID, 0, 1, 0); + } catch (RemoteException e) { + e.printStackTrace(); + } + } + } else { + List faces = getEnrolledFaces(sensorId, userId); + if (faces.isEmpty()) { + Slog.w(TAG, "Ignoring getFeature, no templates enrolled for user: " + userId); + return; + } + scheduleUpdateActiveUserWithoutHandler(userId); + final FaceGetFeatureClient client = new FaceGetFeatureClient(mContext, mLazyDaemon, token, listener, userId, opPackageName, mSensorId, BiometricLogger.ofUnknown(mContext), mBiometricContext, feature, faces.get(0).getBiometricId()); + mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() { + @Override + public void onClientFinished(BaseClientMonitor clientMonitor, boolean success) { + if (success && feature == 1) { + final int settingsValue = client.getValue() ? 1 : 0; + Slog.d(TAG, "Updating attention value for user: " + userId + " to value: " + settingsValue); + Settings.Secure.putIntForUser(mContext.getContentResolver(), "face_unlock_attention_required", settingsValue, userId); + } + } + }); + } + }); + } + + void scheduleInternalCleanup(int userId, ClientMonitorCallback callback) { + mHandler.post(() -> { + scheduleUpdateActiveUserWithoutHandler(userId); + List enrolledList = getEnrolledFaces(mSensorId, userId); + String opPackageName = mContext.getOpPackageName(); + mScheduler.scheduleClientMonitor(new FaceInternalCleanupClient(mContext, mLazyDaemon, userId, opPackageName, mSensorId, createLogger(BiometricsProtoEnums.ACTION_ENUMERATE, BiometricsProtoEnums.CLIENT_UNKNOWN), mBiometricContext, enrolledList, FaceUtils.getLegacyInstance(mSensorId), mAuthenticatorIds), callback); + }); + } + + @Override + public void scheduleInternalCleanup(int sensorId, int userId, ClientMonitorCallback callback) { + scheduleInternalCleanup(userId, callback); + } + + @Override + public void scheduleInvalidateAuthenticatorId(int i, int i1, IInvalidationCallback iInvalidationCallback) { + ServiceProvider.super.scheduleInvalidateAuthenticatorId(i, i1, iInvalidationCallback); + } + + @Override + public void startPreparedClient(int sensorId, int cookie) { + mHandler.post(() -> mScheduler.startPreparedClient(cookie)); + } + + @Override + public void dumpProtoState(int sensorId, ProtoOutputStream proto, boolean clearSchedulerBuffer) { + final long sensorToken = proto.start(SensorServiceStateProto.SENSOR_STATES); + + proto.write(SensorStateProto.SENSOR_ID, mSensorProperties.sensorId); + proto.write(SensorStateProto.MODALITY, SensorStateProto.FACE); + proto.write(SensorStateProto.CURRENT_STRENGTH, + Utils.getCurrentStrength(mSensorProperties.sensorId)); + proto.write(SensorStateProto.SCHEDULER, mScheduler.dumpProtoState(clearSchedulerBuffer)); + + for (UserInfo user : UserManager.get(mContext).getUsers()) { + final int userId = user.getUserHandle().getIdentifier(); + + final long userToken = proto.start(SensorStateProto.USER_STATES); + proto.write(UserStateProto.USER_ID, userId); + proto.write(UserStateProto.NUM_ENROLLED, FaceUtils.getLegacyInstance(mSensorId) + .getBiometricsForUser(mContext, userId).size()); + proto.end(userToken); + } + + proto.write(SensorStateProto.RESET_LOCKOUT_REQUIRES_HARDWARE_AUTH_TOKEN, + mSensorProperties.resetLockoutRequiresHardwareAuthToken); + proto.write(SensorStateProto.RESET_LOCKOUT_REQUIRES_CHALLENGE, + mSensorProperties.resetLockoutRequiresChallenge); + + proto.end(sensorToken); + } + + @Override + public void dumpProtoMetrics(int sensorId, FileDescriptor fd) { + } + + @Override + public void dumpInternal(int sensorId, PrintWriter pw) { + PerformanceTracker performanceTracker = + PerformanceTracker.getInstanceForSensorId(mSensorId); + + JSONObject dump = new JSONObject(); + try { + dump.put("service", TAG); + + JSONArray sets = new JSONArray(); + for (UserInfo user : UserManager.get(mContext).getUsers()) { + final int userId = user.getUserHandle().getIdentifier(); + final int c = FaceUtils.getLegacyInstance(mSensorId) + .getBiometricsForUser(mContext, userId).size(); + JSONObject set = new JSONObject(); + set.put("id", userId); + set.put("count", c); + set.put("accept", performanceTracker.getAcceptForUser(userId)); + set.put("reject", performanceTracker.getRejectForUser(userId)); + set.put("acquire", performanceTracker.getAcquireForUser(userId)); + set.put("lockout", performanceTracker.getTimedLockoutForUser(userId)); + set.put("permanentLockout", performanceTracker.getPermanentLockoutForUser(userId)); + // cryptoStats measures statistics about secure face transactions + // (e.g. to unlock password storage, make secure purchases, etc.) + set.put("acceptCrypto", performanceTracker.getAcceptCryptoForUser(userId)); + set.put("rejectCrypto", performanceTracker.getRejectCryptoForUser(userId)); + set.put("acquireCrypto", performanceTracker.getAcquireCryptoForUser(userId)); + sets.put(set); + } + + dump.put("prints", sets); + } catch (JSONException e) { + Slog.e(TAG, "dump formatting failure", e); + } + pw.println(dump); + pw.println("HAL deaths since last reboot: " + performanceTracker.getHALDeathCount()); + + mScheduler.dump(pw); + mUsageStats.print(pw); + } + + private void scheduleLoadAuthenticatorIds() { + mHandler.post(() -> { + for (UserInfo user : UserManager.get(mContext).getAliveUsers()) { + int targetUserId = user.id; + if (!mAuthenticatorIds.containsKey(Integer.valueOf(targetUserId))) { + scheduleUpdateActiveUserWithoutHandler(targetUserId); + } + } + }); + } + + void scheduleUpdateActiveUserWithoutHandler(final int targetUserId) { + mScheduler.scheduleClientMonitor(new FaceUpdateActiveUserClient(mContext, mLazyDaemon, targetUserId, mContext.getOpPackageName(), mSensorId, createLogger(BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN), mBiometricContext, mCurrentUserId, !getEnrolledFaces(mSensorId, targetUserId).isEmpty(), mAuthenticatorIds), new ClientMonitorCallback() { + @Override + public void onClientFinished(BaseClientMonitor clientMonitor, boolean success) { + if (success) { + mCurrentUserId = targetUserId; + } + } + }); + } + + private boolean isFaceServiceEnabled() { + if (!FaceUnlockUtils.isFaceUnlockSupported()) { + return false; + } + PackageManager pm = mContext.getPackageManager(); + ResolveInfo info = pm.resolveService(FaceUnlockUtils.getServiceIntent(), 131072); + return info != null && info.serviceInfo.isEnabled(); + } + + public static boolean useCustomFaceUnlockService() { + return FaceUnlockUtils.isFaceUnlockSupported(); + } + + private IFaceService getFaceService(int userId) { + if (userId == -10000) { + scheduleUpdateActiveUserWithoutHandler(ActivityManager.getCurrentUser()); + } + return mFaceServices.get(mCurrentUserId); + } + + void bindFaceAuthService(int userId) { + Slog.d(TAG, "bindFaceAuthService " + userId); + if (!isFaceServiceEnabled()) { + Slog.d(TAG, "FaceService disabled"); + } else if (mIsServiceBinding) { + Slog.d(TAG, "FaceService is binding"); + } else { + if (userId != -10000 && getFaceService(userId) == null) { + try { + Intent intent = FaceUnlockUtils.getServiceIntent(); + boolean result = mContext.bindServiceAsUser(intent, new FaceServiceConnection(userId), 1, UserHandle.of(userId)); + if (result) { + mIsServiceBinding = true; + } + } catch (SecurityException e) { + e.printStackTrace(); + } + } + } + } + + @Override + public void dumpHal(int sensorId, FileDescriptor fd, String[] args) { + } + + protected void setTestHalEnabled(boolean enabled) { + mTestHalEnabled = enabled; + } + + @Override + public ITestSession createTestSession(int sensorId, ITestSessionCallback callback, String opPackageName) { + return new BiometricTestSessionImpl(mContext, mSensorId, callback, this, mHalResultController); + } + + public static class HalResultController extends com.android.internal.util.custom.faceunlock.IFaceServiceReceiver.Stub { + private final Context mContext; + private final Handler mHandler; + private final LockoutResetDispatcher mLockoutResetDispatcher; + private final LockoutHalImpl mLockoutTracker; + private final BiometricScheduler mScheduler; + private final int mSensorId; + private Callback mCallback; + + HalResultController(int sensorId, Context context, Handler handler, BiometricScheduler scheduler, LockoutHalImpl lockoutTracker, LockoutResetDispatcher lockoutResetDispatcher) { + mSensorId = sensorId; + mContext = context; + mHandler = handler; + mScheduler = scheduler; + mLockoutTracker = lockoutTracker; + mLockoutResetDispatcher = lockoutResetDispatcher; + } + + public void setCallback(Callback callback) { + mCallback = callback; + } + + public void onEnrollResult(int faceId, int userId, int remaining) { + mHandler.post(() -> { + Face face = new Face(FaceUtils.getLegacyInstance(mSensorId).getUniqueName(mContext, userId), faceId, DEVICE_ID); + BaseClientMonitor client = mScheduler.getCurrentClient(); + if (!(client instanceof FaceEnrollClient)) { + Slog.e(TAG, "onEnrollResult for non-enroll client: " + Utils.getClientName(client)); + return; + } + ((FaceEnrollClient) client).onEnrollResult(face, remaining); + }); + } + + public void onAuthenticated(int faceId, int userId, byte[] token) { + mHandler.post(() -> { + BaseClientMonitor client = mScheduler.getCurrentClient(); + if (!(client instanceof AuthenticationConsumer)) { + Slog.e(TAG, "onAuthenticated for non-authentication consumer: " + Utils.getClientName(client)); + return; + } + ((AuthenticationConsumer) client).onAuthenticated(new Face("", faceId, DEVICE_ID), faceId != 0, ArrayUtils.toByteArrayList(token)); + }); + } + + public void onAcquired(int userId, int acquiredInfo, int vendorCode) { + mHandler.post(() -> { + BaseClientMonitor client = mScheduler.getCurrentClient(); + if (!(client instanceof AcquisitionClient)) { + Slog.e(TAG, "onAcquired for non-acquire client: " + Utils.getClientName(client)); + return; + } + final AcquisitionClient acquisitionClient = + (AcquisitionClient) client; + acquisitionClient.onAcquired(acquiredInfo, vendorCode); + }); + } + + public void onError(int error, int vendorCode) { + mHandler.post(() -> { + BaseClientMonitor client = mScheduler.getCurrentClient(); + String log = "handleError, client: " + + (client != null ? client.getOwnerString() : null) + + ", error: " + + error + + ", vendorCode: " + + vendorCode; + Slog.d(TAG, log); + if (!(client instanceof ErrorConsumer)) { + Slog.e(TAG, "onError for non-error consumer: " + Utils.getClientName(client)); + return; + } + ((ErrorConsumer) client).onError(error, vendorCode); + if (error == 1) { + Slog.e(TAG, "Got ERROR_HW_UNAVAILABLE"); + if (mCallback != null) { + mCallback.onHardwareUnavailable(); + } + } + }); + } + + public void onRemoved(int[] removed, int userId) { + mHandler.post(() -> { + BaseClientMonitor client = mScheduler.getCurrentClient(); + if (!(client instanceof RemovalConsumer)) { + Slog.e(TAG, "onRemoved for non-removal consumer: " + Utils.getClientName(client)); + return; + } + RemovalConsumer removalConsumer = (RemovalConsumer) client; + if (removed.length > 0) { + for (int i = 0; i < removed.length; i++) { + int id = removed[i]; + Face face = new Face("", id, DEVICE_ID); + int remaining = (removed.length - i) - 1; + Slog.d(TAG, "Removed, faceId: " + id + ", remaining: " + remaining); + removalConsumer.onRemoved(face, remaining); + } + } else { + removalConsumer.onRemoved(null, 0); + } + Settings.Secure.putIntForUser(mContext.getContentResolver(), "face_unlock_re_enroll", 0, -2); + }); + } + + public void onEnumerate(int[] faceIds, int userId) { + mHandler.post(() -> { + BaseClientMonitor client = mScheduler.getCurrentClient(); + if (!(client instanceof EnumerateConsumer)) { + Slog.e(TAG, "onEnumerate for non-enumerate consumer: " + Utils.getClientName(client)); + return; + } + EnumerateConsumer enumerateConsumer = (EnumerateConsumer) client; + if (faceIds.length > 0) { + for (int i = 0; i < faceIds.length; i++) { + enumerateConsumer.onEnumerationResult(new Face("", faceIds[i], DEVICE_ID), (faceIds.length - i) - 1); + } + return; + } + enumerateConsumer.onEnumerationResult(null, 0); + }); + } + + public void onLockoutChanged(long duration) { + mHandler.post(() -> { + int lockoutMode; + Slog.d(TAG, "onLockoutChanged: " + duration); + if (duration == 0) { + lockoutMode = 0; + } else if (duration == -1 || duration == Long.MAX_VALUE) { + lockoutMode = 2; + } else { + lockoutMode = 1; + } + mLockoutTracker.setCurrentUserLockoutMode(lockoutMode); + if (duration == 0) { + mLockoutResetDispatcher.notifyLockoutResetCallbacks(mSensorId); + } + }); + } + + public interface Callback { + void onHardwareUnavailable(); + } + } + + class FaceServiceConnection implements ServiceConnection { + private final int mUserId; + + public FaceServiceConnection(int userId) { + mUserId = userId; + } + + @Override + public void onServiceConnected(ComponentName className, IBinder service) { + Slog.d(TAG, "FaceService connected : " + mUserId); + IFaceService faceService = IFaceService.Stub.asInterface(service); + if (faceService != null) { + synchronized (mFaceServices) { + try { + faceService.setCallback(mHalResultController); + mFaceServices.put(mUserId, faceService); + mHandler.post(() -> { + scheduleInternalCleanup(mUserId, null); + scheduleGetFeature(mSensorId, new Binder(), mUserId, 1, null, mContext.getOpPackageName()); + }); + } catch (RemoteException e) { + e.printStackTrace(); + } + mIsServiceBinding = false; + } + } + } + + @Override + public void onServiceDisconnected(ComponentName className) { + Slog.d(TAG, "FaceService disconnected : " + mUserId); + mFaceServices.remove(mUserId); + mIsServiceBinding = false; + if (mUserId == mCurrentUserId) { + mHandler.postDelayed(() -> { + BaseClientMonitor client = mScheduler.getCurrentClient(); + if (client instanceof ErrorConsumer) { + ((ErrorConsumer) client).onError(5, 0); + } + bindFaceAuthService(mUserId); + mScheduler.recordCrashState(); + mScheduler.reset(); + }, 100); + } + mContext.unbindService(this); + } + } + + private BiometricLogger createLogger(int statsAction, int statsClient) { + return new BiometricLogger(mContext, BiometricsProtoEnums.MODALITY_FACE, + statsAction, statsClient); + } +} diff --git a/services/core/java/com/android/server/biometrics/sensors/face/custom/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/custom/FaceAuthenticationClient.java new file mode 100644 index 000000000000..5c01d7b20f6a --- /dev/null +++ b/services/core/java/com/android/server/biometrics/sensors/face/custom/FaceAuthenticationClient.java @@ -0,0 +1,141 @@ +/* +* Copyright (C) 2022 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.server.biometrics.sensors.face.custom; + +import android.content.ContentResolver; +import android.content.Context; +import android.content.res.Resources; +import android.hardware.biometrics.BiometricAuthenticator; +import android.os.IBinder; +import android.os.RemoteException; +import android.provider.Settings; +import android.util.Slog; + +import com.android.internal.R; +import com.android.internal.util.custom.faceunlock.IFaceService; +import com.android.server.biometrics.Utils; +import com.android.server.biometrics.sensors.AuthenticationClient; +import com.android.server.biometrics.sensors.BiometricNotificationUtils; +import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; +import com.android.server.biometrics.sensors.LockoutTracker; +import com.android.server.biometrics.sensors.face.UsageStats; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; + +import java.util.ArrayList; +import java.util.function.Supplier; + +class FaceAuthenticationClient extends AuthenticationClient { + private static final String TAG = "FaceAuthenticationClient"; + private final int[] mBiometricPromptIgnoreList; + private final int[] mBiometricPromptIgnoreListVendor; + private final ContentResolver mContentResolver; + private final boolean mCustomHaptics; + private final int[] mKeyguardIgnoreList; + private final int[] mKeyguardIgnoreListVendor; + private final UsageStats mUsageStats; + private int mLastAcquire; + + FaceAuthenticationClient(Context context, Supplier lazyDaemon, IBinder token, long requestId, ClientMonitorCallbackConverter listener, int targetUserId, long operationId, boolean restricted, String owner, int cookie, boolean requireConfirmation, int sensorId, BiometricLogger biometricLogger, BiometricContext biometricContext, boolean isStrongBiometric, LockoutTracker lockoutTracker, UsageStats usageStats, boolean allowBackgroundAuthentication) { + super(context, lazyDaemon, token, listener, targetUserId, operationId, restricted, owner, cookie, requireConfirmation, sensorId, biometricLogger, biometricContext, isStrongBiometric, null /* taskStackListener */, lockoutTracker, allowBackgroundAuthentication, true, false); + mUsageStats = usageStats; + setRequestId(requestId); + Resources resources = getContext().getResources(); + mBiometricPromptIgnoreList = resources.getIntArray( + R.array.config_face_acquire_biometricprompt_ignorelist); + mBiometricPromptIgnoreListVendor = resources.getIntArray( + R.array.config_face_acquire_vendor_biometricprompt_ignorelist); + mKeyguardIgnoreList = resources.getIntArray( + R.array.config_face_acquire_keyguard_ignorelist); + mKeyguardIgnoreListVendor = resources.getIntArray( + R.array.config_face_acquire_vendor_keyguard_ignorelist); + ContentResolver contentResolver = context.getContentResolver(); + mContentResolver = contentResolver; + mCustomHaptics = Settings.Global.getInt(contentResolver, "face_custom_success_error", 0) == 1; + } + + @Override + protected void startHalOperation() { + try { + getFreshDaemon().authenticate(mOperationId); + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception when requesting auth", e); + onError(1, 0); + mCallback.onClientFinished(this, false); + } + } + + @Override + protected void handleLifecycleAfterAuth(boolean authenticated) { + } + + @Override + protected void stopHalOperation() { + try { + getFreshDaemon().cancel(); + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception when requesting cancel", e); + onError(1, 0); + mCallback.onClientFinished(this, false); + } + } + + @Override + public boolean wasUserDetected() { + return mLastAcquire != 11 && mLastAcquire != 21; + } + + @Override + public void onAuthenticated(BiometricAuthenticator.Identifier identifier, boolean authenticated, ArrayList token) { + super.onAuthenticated(identifier, authenticated, token); + mUsageStats.addEvent(new UsageStats.AuthenticationEvent(getStartTimeMs(), System.currentTimeMillis() - getStartTimeMs(), authenticated, 0, 0, getTargetUserId())); + mCallback.onClientFinished(this, true); + } + + @Override + public void onError(int error, int vendorCode) { + mUsageStats.addEvent(new UsageStats.AuthenticationEvent(getStartTimeMs(), System.currentTimeMillis() - getStartTimeMs(), false, error, vendorCode, getTargetUserId())); + if (error == 16) { + BiometricNotificationUtils.showReEnrollmentNotification(getContext()); + } + super.onError(error, vendorCode); + } + + private int[] getAcquireIgnorelist() { + return isBiometricPrompt() ? mBiometricPromptIgnoreList : mKeyguardIgnoreList; + } + + private int[] getAcquireVendorIgnorelist() { + return isBiometricPrompt() ? mBiometricPromptIgnoreListVendor : mKeyguardIgnoreListVendor; + } + + private boolean shouldSend(int acquireInfo, int vendorCode) { + if (acquireInfo == 22) { + return !Utils.listContains(getAcquireVendorIgnorelist(), vendorCode); + } + return !Utils.listContains(getAcquireIgnorelist(), acquireInfo); + } + + @Override + public void onAcquired(int acquireInfo, int vendorCode) { + mLastAcquire = acquireInfo; + if (acquireInfo == 13) { + BiometricNotificationUtils.showReEnrollmentNotification(getContext()); + } + onAcquiredInternal(acquireInfo, vendorCode, shouldSend(acquireInfo, vendorCode)); + } +} diff --git a/services/core/java/com/android/server/biometrics/sensors/face/custom/FaceEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/face/custom/FaceEnrollClient.java new file mode 100644 index 000000000000..6272ce7ec32c --- /dev/null +++ b/services/core/java/com/android/server/biometrics/sensors/face/custom/FaceEnrollClient.java @@ -0,0 +1,101 @@ +/* +* Copyright (C) 2022 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.server.biometrics.sensors.face.custom; + +import android.content.Context; +import android.hardware.face.Face; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Slog; +import android.view.Surface; + +import com.android.internal.R; +import com.android.internal.util.custom.faceunlock.IFaceService; +import com.android.server.biometrics.Utils; +import com.android.server.biometrics.sensors.BiometricUtils; +import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; +import com.android.server.biometrics.sensors.EnrollClient; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.function.Supplier; + +class FaceEnrollClient extends EnrollClient { + private static final String TAG = "FaceEnrollClient"; + private final int[] mDisabledFeatures; + private final int[] mEnrollIgnoreList = getContext().getResources().getIntArray(R.array.config_face_acquire_enroll_ignorelist); + private final int[] mEnrollIgnoreListVendor = getContext().getResources().getIntArray(R.array.config_face_acquire_vendor_enroll_ignorelist); + private final Surface mPreviewSurface; + + FaceEnrollClient(Context context, Supplier lazyDaemon, IBinder token, ClientMonitorCallbackConverter listener, int userId, byte[] hardwareAuthToken, String owner, BiometricUtils utils, int[] disabledFeatures, int timeoutSec, Surface previewSurface, int sensorId, BiometricLogger biometricLogger, BiometricContext biometricContext) { + super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils, timeoutSec, sensorId, false /* shouldVibrate */, biometricLogger, biometricContext); + mDisabledFeatures = Arrays.copyOf(disabledFeatures, disabledFeatures.length); + mPreviewSurface = previewSurface; + } + + @Override + protected boolean hasReachedEnrollmentLimit() { + if (mBiometricUtils.getBiometricsForUser(getContext(), getTargetUserId()).size() < getContext().getResources().getInteger(R.integer.config_faceMaxTemplatesPerUser)) { + return false; + } + Slog.w(TAG, "Too many faces registered, user: " + getTargetUserId()); + return true; + } + + @Override + public void onAcquired(int acquireInfo, int vendorCode) { + boolean shouldSend; + if (acquireInfo == 22) { + shouldSend = !Utils.listContains(mEnrollIgnoreListVendor, vendorCode); + } else { + shouldSend = !Utils.listContains(mEnrollIgnoreList, acquireInfo); + } + onAcquiredInternal(acquireInfo, vendorCode, shouldSend); + } + + @Override + protected void startHalOperation() { + ArrayList token = new ArrayList<>(); + for (byte b : mHardwareAuthToken) { + token.add(b); + } + ArrayList disabledFeatures = new ArrayList<>(); + for (int disabledFeature : mDisabledFeatures) { + disabledFeatures.add(disabledFeature); + } + try { + getFreshDaemon().enroll(ArrayUtils.toByteArray(token), mTimeoutSec, ArrayUtils.toIntArray(disabledFeatures)); + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception when requesting enroll", e); + onError(2, 0); + mCallback.onClientFinished(this, false); + } + } + + @Override + protected void stopHalOperation() { + try { + getFreshDaemon().cancel(); + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception when requesting cancel", e); + onError(1, 0); + mCallback.onClientFinished(this, false); + } + } +} diff --git a/services/core/java/com/android/server/biometrics/sensors/face/custom/FaceGenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/face/custom/FaceGenerateChallengeClient.java new file mode 100644 index 000000000000..44c4e6809c07 --- /dev/null +++ b/services/core/java/com/android/server/biometrics/sensors/face/custom/FaceGenerateChallengeClient.java @@ -0,0 +1,94 @@ +/* +* Copyright (C) 2022 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.server.biometrics.sensors.face.custom; + +import android.content.Context; +import android.hardware.face.IFaceServiceReceiver; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Slog; + +import com.android.internal.util.Preconditions; +import com.android.internal.util.custom.faceunlock.IFaceService; +import com.android.server.biometrics.sensors.BaseClientMonitor; +import com.android.server.biometrics.sensors.ClientMonitorCallback; +import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; +import com.android.server.biometrics.sensors.GenerateChallengeClient; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Supplier; + +class FaceGenerateChallengeClient extends GenerateChallengeClient { + static final int CHALLENGE_TIMEOUT_SEC = 600; + private static final ClientMonitorCallback EMPTY_CALLBACK = new ClientMonitorCallback() { + }; + private static final String TAG = "FaceGenerateChallengeClient"; + private final long mCreatedAt; + private Long mChallengeResult; + private List mWaiting = new ArrayList(); + + FaceGenerateChallengeClient(Context context, Supplier lazyDaemon, IBinder token, ClientMonitorCallbackConverter listener, int userId, String owner, int sensorId, BiometricLogger biometricLogger, BiometricContext biometricContext, long now) { + super(context, lazyDaemon, token, listener, userId, owner, sensorId, biometricLogger, biometricContext); + mCreatedAt = now; + } + + @Override + protected void startHalOperation() { + mChallengeResult = null; + try { + try { + mChallengeResult = getFreshDaemon().generateChallenge(600); + sendChallengeResult(getListener(), mCallback); + for (IFaceServiceReceiver receiver : mWaiting) { + sendChallengeResult(new ClientMonitorCallbackConverter(receiver), EMPTY_CALLBACK); + } + } catch (RemoteException e) { + Slog.e(TAG, "generateChallenge failed", e); + mCallback.onClientFinished(this, false); + } + } finally { + mWaiting = null; + } + } + + public long getCreatedAt() { + return mCreatedAt; + } + + public void reuseResult(IFaceServiceReceiver receiver) { + List list = mWaiting; + if (list != null) { + list.add(receiver); + } else { + sendChallengeResult(new ClientMonitorCallbackConverter(receiver), EMPTY_CALLBACK); + } + } + + private void sendChallengeResult(ClientMonitorCallbackConverter receiver, ClientMonitorCallback ownerCallback) { + Preconditions.checkState(mChallengeResult != null, "result not available"); + try { + receiver.onChallengeGenerated(getSensorId(), getTargetUserId(), mChallengeResult); + ownerCallback.onClientFinished(this, true); + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception", e); + ownerCallback.onClientFinished(this, false); + } + } +} diff --git a/services/core/java/com/android/server/biometrics/sensors/face/custom/FaceGetFeatureClient.java b/services/core/java/com/android/server/biometrics/sensors/face/custom/FaceGetFeatureClient.java new file mode 100644 index 000000000000..901bf581fa13 --- /dev/null +++ b/services/core/java/com/android/server/biometrics/sensors/face/custom/FaceGetFeatureClient.java @@ -0,0 +1,88 @@ +/* +* Copyright (C) 2022 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.server.biometrics.sensors.face.custom; + +import android.content.Context; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Slog; + +import com.android.internal.util.custom.faceunlock.IFaceService; +import com.android.server.biometrics.sensors.BaseClientMonitor; +import com.android.server.biometrics.sensors.ClientMonitorCallback; +import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; +import com.android.server.biometrics.sensors.HalClientMonitor; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; + +import java.util.function.Supplier; + +public class FaceGetFeatureClient extends HalClientMonitor { + private static final String TAG = "FaceGetFeatureClient"; + private final int mFaceId; + private final int mFeature; + private boolean mValue; + + FaceGetFeatureClient(Context context, Supplier lazyDaemon, IBinder token, ClientMonitorCallbackConverter listener, int userId, String owner, int sensorId, BiometricLogger biometricLogger, BiometricContext biometricContext, int feature, int faceId) { + super(context, lazyDaemon, token, listener, userId, owner, 0, sensorId, biometricLogger, biometricContext); + mFeature = feature; + mFaceId = faceId; + } + + @Override + public void unableToStart() { + try { + if (getListener() != null) { + getListener().onFeatureGet(false, new int[0], new boolean[0]); + } + } catch (RemoteException e) { + Slog.e(TAG, "Unable to send error", e); + } + } + + @Override + public void start(ClientMonitorCallback callback) { + super.start(callback); + startHalOperation(); + } + + @Override + protected void startHalOperation() { + try { + boolean result = getFreshDaemon().getFeature(mFeature, mFaceId); + int[] features = {mFeature}; + boolean[] featureState = {result}; + mValue = result; + if (getListener() != null) { + getListener().onFeatureGet(result, features, featureState); + } + mCallback.onClientFinished(this, true); + } catch (RemoteException e) { + Slog.e(TAG, "Unable to getFeature", e); + mCallback.onClientFinished(this, false); + } + } + + public boolean getValue() { + return mValue; + } + + @Override + public int getProtoEnum() { + return 9; + } +} diff --git a/services/core/java/com/android/server/biometrics/sensors/face/custom/FaceInternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/face/custom/FaceInternalCleanupClient.java new file mode 100644 index 000000000000..662886d79ca5 --- /dev/null +++ b/services/core/java/com/android/server/biometrics/sensors/face/custom/FaceInternalCleanupClient.java @@ -0,0 +1,49 @@ +/* +* Copyright (C) 2022 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.server.biometrics.sensors.face.custom; + +import android.content.Context; +import android.hardware.face.Face; +import android.os.IBinder; + +import com.android.internal.util.custom.faceunlock.IFaceService; +import com.android.server.biometrics.sensors.BiometricUtils; +import com.android.server.biometrics.sensors.InternalCleanupClient; +import com.android.server.biometrics.sensors.InternalEnumerateClient; +import com.android.server.biometrics.sensors.RemovalClient; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; + +import java.util.List; +import java.util.Map; +import java.util.function.Supplier; + +class FaceInternalCleanupClient extends InternalCleanupClient { + FaceInternalCleanupClient(Context context, Supplier lazyDaemon, int userId, String owner, int sensorId, BiometricLogger biometricLogger, BiometricContext biometricContext, List enrolledList, BiometricUtils utils, Map authenticatorIds) { + super(context, lazyDaemon, userId, owner, sensorId, biometricLogger, biometricContext, enrolledList, utils, authenticatorIds); + } + + @Override + protected InternalEnumerateClient getEnumerateClient(Context context, Supplier lazyDaemon, IBinder token, int userId, String owner, List enrolledList, BiometricUtils utils, int sensorId, BiometricLogger biometricLogger, BiometricContext biometricContext) { + return new FaceInternalEnumerateClient(context, lazyDaemon, token, userId, owner, enrolledList, utils, sensorId, biometricLogger, biometricContext); + } + + @Override + protected RemovalClient getRemovalClient(Context context, Supplier lazyDaemon, IBinder token, int biometricId, int userId, String owner, BiometricUtils utils, int sensorId, BiometricLogger biometricLogger, BiometricContext biometricContext, Map authenticatorIds) { + return new FaceRemovalClient(context, lazyDaemon, token, null, biometricId, userId, owner, utils, sensorId, biometricLogger, biometricContext, authenticatorIds); + } +} diff --git a/services/core/java/com/android/server/biometrics/sensors/face/custom/FaceInternalEnumerateClient.java b/services/core/java/com/android/server/biometrics/sensors/face/custom/FaceInternalEnumerateClient.java new file mode 100644 index 000000000000..68b1d3b1f055 --- /dev/null +++ b/services/core/java/com/android/server/biometrics/sensors/face/custom/FaceInternalEnumerateClient.java @@ -0,0 +1,50 @@ +/* +* Copyright (C) 2022 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.server.biometrics.sensors.face.custom; + +import android.content.Context; +import android.hardware.face.Face; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Slog; + +import com.android.internal.util.custom.faceunlock.IFaceService; +import com.android.server.biometrics.sensors.BiometricUtils; +import com.android.server.biometrics.sensors.InternalEnumerateClient; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; + +import java.util.List; +import java.util.function.Supplier; + +class FaceInternalEnumerateClient extends InternalEnumerateClient { + private static final String TAG = "FaceInternalEnumerateClient"; + + FaceInternalEnumerateClient(Context context, Supplier lazyDaemon, IBinder token, int userId, String owner, List enrolledList, BiometricUtils utils, int sensorId, BiometricLogger biometricLogger, BiometricContext biometricContext) { + super(context, lazyDaemon, token, userId, owner, enrolledList, utils, sensorId, biometricLogger, biometricContext); + } + + @Override + protected void startHalOperation() { + try { + getFreshDaemon().enumerate(); + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception when requesting enumerate", e); + mCallback.onClientFinished(this, false); + } + } +} diff --git a/services/core/java/com/android/server/biometrics/sensors/face/custom/FaceRemovalClient.java b/services/core/java/com/android/server/biometrics/sensors/face/custom/FaceRemovalClient.java new file mode 100644 index 000000000000..61a7976fc0c6 --- /dev/null +++ b/services/core/java/com/android/server/biometrics/sensors/face/custom/FaceRemovalClient.java @@ -0,0 +1,53 @@ +/* +* Copyright (C) 2022 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.server.biometrics.sensors.face.custom; + +import android.content.Context; +import android.hardware.face.Face; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Slog; + +import com.android.internal.util.custom.faceunlock.IFaceService; +import com.android.server.biometrics.sensors.BiometricUtils; +import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; +import com.android.server.biometrics.sensors.RemovalClient; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; + +import java.util.Map; +import java.util.function.Supplier; + +class FaceRemovalClient extends RemovalClient { + private static final String TAG = "FaceRemovalClient"; + private final int mBiometricId; + + FaceRemovalClient(Context context, Supplier lazyDaemon, IBinder token, ClientMonitorCallbackConverter listener, int biometricId, int userId, String owner, BiometricUtils utils, int sensorId, BiometricLogger biometricLogger, BiometricContext biometricContext, Map authenticatorIds) { + super(context, lazyDaemon, token, listener, userId, owner, utils, sensorId, biometricLogger, biometricContext, authenticatorIds); + mBiometricId = biometricId; + } + + @Override + protected void startHalOperation() { + try { + getFreshDaemon().remove(mBiometricId); + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception when requesting remove", e); + mCallback.onClientFinished(this, false); + } + } +} diff --git a/services/core/java/com/android/server/biometrics/sensors/face/custom/FaceResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/face/custom/FaceResetLockoutClient.java new file mode 100644 index 000000000000..5915f81056f5 --- /dev/null +++ b/services/core/java/com/android/server/biometrics/sensors/face/custom/FaceResetLockoutClient.java @@ -0,0 +1,71 @@ +/* +* Copyright (C) 2022 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.server.biometrics.sensors.face.custom; + +import android.content.Context; +import android.os.RemoteException; +import android.util.Slog; + +import com.android.internal.util.custom.faceunlock.IFaceService; +import com.android.server.biometrics.sensors.BaseClientMonitor; +import com.android.server.biometrics.sensors.ClientMonitorCallback; +import com.android.server.biometrics.sensors.HalClientMonitor; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; + +import java.util.function.Supplier; + +class FaceResetLockoutClient extends HalClientMonitor { + private static final String TAG = "FaceResetLockoutClient"; + private final byte[] mHardwareAuthToken; + + FaceResetLockoutClient(Context context, Supplier lazyDaemon, int userId, String owner, int sensorId, BiometricLogger biometricLogger, BiometricContext biometricContext, byte[] hardwareAuthToken) { + super(context, lazyDaemon, null, null, userId, owner, 0, sensorId, biometricLogger, biometricContext); + mHardwareAuthToken = hardwareAuthToken.clone(); + } + + @Override + public void unableToStart() { + } + + @Override + public void start(ClientMonitorCallback callback) { + super.start(callback); + startHalOperation(); + } + + @Override + protected void startHalOperation() { + try { + getFreshDaemon().resetLockout(mHardwareAuthToken); + mCallback.onClientFinished(this, true); + } catch (RemoteException e) { + Slog.e(TAG, "Unable to reset lockout", e); + mCallback.onClientFinished(this, false); + } + } + + @Override + public int getProtoEnum() { + return 12; + } + + @Override + public boolean interruptsPrecedingClients() { + return true; + } +} diff --git a/services/core/java/com/android/server/biometrics/sensors/face/custom/FaceRevokeChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/face/custom/FaceRevokeChallengeClient.java new file mode 100644 index 000000000000..7939bf3d75b1 --- /dev/null +++ b/services/core/java/com/android/server/biometrics/sensors/face/custom/FaceRevokeChallengeClient.java @@ -0,0 +1,48 @@ +/* +* Copyright (C) 2022 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.server.biometrics.sensors.face.custom; + +import android.content.Context; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Slog; + +import com.android.internal.util.custom.faceunlock.IFaceService; +import com.android.server.biometrics.sensors.RevokeChallengeClient; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; + +import java.util.function.Supplier; + +class FaceRevokeChallengeClient extends RevokeChallengeClient { + private static final String TAG = "FaceRevokeChallengeClient"; + + FaceRevokeChallengeClient(Context context, Supplier lazyDaemon, IBinder token, int userId, String owner, int sensorId, BiometricLogger biometricLogger, BiometricContext biometricContext) { + super(context, lazyDaemon, token, userId, owner, sensorId, biometricLogger, biometricContext); + } + + @Override + protected void startHalOperation() { + try { + getFreshDaemon().revokeChallenge(); + mCallback.onClientFinished(this, true); + } catch (RemoteException e) { + Slog.e(TAG, "revokeChallenge failed", e); + mCallback.onClientFinished(this, false); + } + } +} diff --git a/services/core/java/com/android/server/biometrics/sensors/face/custom/FaceSetFeatureClient.java b/services/core/java/com/android/server/biometrics/sensors/face/custom/FaceSetFeatureClient.java new file mode 100644 index 000000000000..174cfca92982 --- /dev/null +++ b/services/core/java/com/android/server/biometrics/sensors/face/custom/FaceSetFeatureClient.java @@ -0,0 +1,80 @@ +/* +* Copyright (C) 2022 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.server.biometrics.sensors.face.custom; + +import android.content.Context; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Slog; + +import com.android.internal.util.custom.faceunlock.IFaceService; +import com.android.server.biometrics.sensors.BaseClientMonitor; +import com.android.server.biometrics.sensors.ClientMonitorCallback; +import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; +import com.android.server.biometrics.sensors.HalClientMonitor; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; + +import java.util.function.Supplier; + +class FaceSetFeatureClient extends HalClientMonitor { + private static final String TAG = "FaceSetFeatureClient"; + private final boolean mEnabled; + private final int mFaceId; + private final int mFeature; + private final byte[] mHardwareAuthToken; + + FaceSetFeatureClient(Context context, Supplier lazyDaemon, IBinder token, ClientMonitorCallbackConverter listener, int userId, String owner, int sensorId, BiometricLogger biometricLogger, BiometricContext biometricContext, int feature, boolean enabled, byte[] hardwareAuthToken, int faceId) { + super(context, lazyDaemon, token, listener, userId, owner, 0, sensorId, biometricLogger, biometricContext); + mFeature = feature; + mEnabled = enabled; + mFaceId = faceId; + mHardwareAuthToken = hardwareAuthToken.clone(); + } + + @Override + public void unableToStart() { + try { + getListener().onFeatureSet(false, mFeature); + } catch (RemoteException e) { + Slog.e(TAG, "Unable to send error", e); + } + } + + @Override + public void start(ClientMonitorCallback callback) { + super.start(callback); + startHalOperation(); + } + + @Override + protected void startHalOperation() { + try { + getFreshDaemon().setFeature(mFeature, mEnabled, mHardwareAuthToken, mFaceId); + getListener().onFeatureSet(true, mFeature); + mCallback.onClientFinished(this, true); + } catch (RemoteException e) { + Slog.e(TAG, "Unable to set feature: " + mFeature + " to enabled: " + mEnabled, e); + mCallback.onClientFinished(this, false); + } + } + + @Override + public int getProtoEnum() { + return 8; + } +} diff --git a/services/core/java/com/android/server/biometrics/sensors/face/custom/FaceUpdateActiveUserClient.java b/services/core/java/com/android/server/biometrics/sensors/face/custom/FaceUpdateActiveUserClient.java new file mode 100644 index 000000000000..163c24bd7934 --- /dev/null +++ b/services/core/java/com/android/server/biometrics/sensors/face/custom/FaceUpdateActiveUserClient.java @@ -0,0 +1,76 @@ +/* +* Copyright (C) 2022 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.server.biometrics.sensors.face.custom; + +import android.content.Context; +import android.os.RemoteException; +import android.util.Slog; + +import com.android.internal.util.custom.faceunlock.IFaceService; +import com.android.server.biometrics.sensors.BaseClientMonitor; +import com.android.server.biometrics.sensors.ClientMonitorCallback; +import com.android.server.biometrics.sensors.HalClientMonitor; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; + +import java.util.Map; +import java.util.function.Supplier; + +class FaceUpdateActiveUserClient extends HalClientMonitor { + private static final String FACE_DATA_DIR = "facedata"; + private static final String TAG = "FaceUpdateActiveUserClient"; + private final Map mAuthenticatorIds; + private final int mCurrentUserId; + private final boolean mHasEnrolledBiometrics; + + FaceUpdateActiveUserClient(Context context, Supplier lazyDaemon, int userId, String owner, int sensorId, BiometricLogger biometricLogger, BiometricContext biometricContext, int currentUserId, boolean hasEnrolledBIometrics, Map authenticatorIds) { + super(context, lazyDaemon, null, null, userId, owner, 0, sensorId, biometricLogger, biometricContext); + mCurrentUserId = currentUserId; + mHasEnrolledBiometrics = hasEnrolledBIometrics; + mAuthenticatorIds = authenticatorIds; + } + + @Override + public void start(ClientMonitorCallback callback) { + super.start(callback); + if (mCurrentUserId == getTargetUserId()) { + Slog.d(TAG, "Already user: " + mCurrentUserId + ", refreshing authenticatorId"); + try { + mAuthenticatorIds.put(getTargetUserId(), mHasEnrolledBiometrics ? (long) getFreshDaemon().getAuthenticatorId() : 0); + } catch (RemoteException e) { + Slog.e(TAG, "Unable to refresh authenticatorId", e); + } + callback.onClientFinished(this, true); + return; + } + startHalOperation(); + } + + @Override + public void unableToStart() { + } + + @Override + protected void startHalOperation() { + mCallback.onClientFinished(this, false); + } + + @Override + public int getProtoEnum() { + return 1; + } +} diff --git a/services/core/java/com/android/server/biometrics/sensors/face/custom/TestHal.java b/services/core/java/com/android/server/biometrics/sensors/face/custom/TestHal.java new file mode 100644 index 000000000000..5b8a9eb24629 --- /dev/null +++ b/services/core/java/com/android/server/biometrics/sensors/face/custom/TestHal.java @@ -0,0 +1,128 @@ +/* +* Copyright (C) 2022 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.server.biometrics.sensors.face.custom; + +import android.content.Context; +import android.hardware.face.Face; +import android.os.RemoteException; +import android.util.Slog; + +import com.android.internal.util.custom.faceunlock.IFaceService; +import com.android.internal.util.custom.faceunlock.IFaceServiceReceiver; +import com.android.server.biometrics.sensors.face.FaceUtils; + +import java.util.List; + +class TestHal extends IFaceService.Stub { + private static final String TAG = "FaceService.TestHal"; + private final Context mContext; + private final int mSensorId; + private final int mUserId; + private IFaceServiceReceiver mCallback; + + TestHal(int userId, Context context, int sensorId) { + mUserId = userId; + mContext = context; + mSensorId = sensorId; + } + + @Override + public void setCallback(IFaceServiceReceiver clientCallback) throws RemoteException { + mCallback = clientCallback; + } + + @Override + public int revokeChallenge() { + return 0; + } + + @Override + public int getAuthenticatorId() throws RemoteException { + return 0; + } + + @Override + public boolean getFeature(int i, int i1) throws RemoteException { + return false; + } + + @Override + public int getFeatureCount() throws RemoteException { + return 0; + } + + @Override + public long generateChallenge(int i) throws RemoteException { + Slog.w(TAG, "generateChallenge"); + return 0; + } + + @Override + public void resetLockout(byte[] bytes) throws RemoteException { + } + + @Override + public void setFeature(int i, boolean b, byte[] bytes, int i1) throws RemoteException { + } + + @Override + public int enumerate() throws RemoteException { + Slog.w(TAG, "enumerate"); + if (mCallback != null) { + mCallback.onEnumerate(new int[0], 0); + } + return 0; + } + + @Override + public void enroll(byte[] bytes, int i, int[] ints) throws RemoteException { + Slog.w(TAG, "enroll"); + } + + @Override + public void authenticate(long l) throws RemoteException { + Slog.w(TAG, "authenticate"); + } + + @Override + public void cancel() throws RemoteException { + if (mCallback != null) { + mCallback.onError(5, 0); + } + } + + @Override + public void remove(int faceId) throws RemoteException { + if (mCallback != null) { + Slog.d(TAG, " remove : faceId = " + faceId); + if (faceId == 0) { + List faces = FaceUtils.getInstance(mSensorId).getBiometricsForUser(mContext, mUserId); + if (faces.size() <= 0) { + mCallback.onError(6, 0); + return; + } + int[] faceIds = new int[faces.size()]; + for (int i = 0; i < faces.size(); i++) { + faceIds[i] = faces.get(i).getBiometricId(); + } + mCallback.onRemoved(faceIds, mUserId); + return; + } + mCallback.onRemoved(new int[]{faceId}, mUserId); + } + } +} diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java index 73c759f7738c..4e630390bd6c 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java @@ -370,7 +370,7 @@ public static Face10 newInstance(@NonNull Context context, @NonNull LockoutResetDispatcher lockoutResetDispatcher) { final Handler handler = new Handler(Looper.getMainLooper()); return new Face10(context, sensorProps, lockoutResetDispatcher, handler, - new BiometricScheduler(TAG, BiometricScheduler.SENSOR_TYPE_FACE, + new BiometricScheduler(context, TAG, BiometricScheduler.SENSOR_TYPE_FACE, null /* gestureAvailabilityTracker */), BiometricContext.getInstance(context)); } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java index b3f42be41cd7..5ff4496e1553 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java @@ -257,7 +257,7 @@ public void onAuthenticated( public void onAcquired(@FingerprintAcquired int acquiredInfo, int vendorCode) { // For UDFPS, notify SysUI with acquiredInfo, so that the illumination can be turned off // for most ACQUIRED messages. See BiometricFingerprintConstants#FingerprintAcquired - mSensorOverlays.ifUdfps(controller -> controller.onAcquired(getSensorId(), acquiredInfo)); + mSensorOverlays.ifUdfps(controller -> controller.onAcquired(getSensorId(), acquiredInfo, vendorCode)); super.onAcquired(acquiredInfo, vendorCode); if (mSensorProps.isAnySidefpsType()) { final boolean shouldLookForVendor = diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java index 612d90670888..d43e91c2c78e 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java @@ -134,7 +134,7 @@ public void onAcquired(@FingerprintAcquired int acquiredInfo, int vendorCode) { vibrateSuccess(); } mSensorOverlays.ifUdfps( - controller -> controller.onAcquired(getSensorId(), acquiredInfo)); + controller -> controller.onAcquired(getSensorId(), acquiredInfo, vendorCode)); } mSensorOverlays.ifUdfps(controller -> { diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java index 6f6c09b91a66..77bacf2865b7 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java @@ -89,6 +89,8 @@ @SuppressWarnings("deprecation") public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvider { + private static final String TAG = "FingerprintProvider"; + private boolean mTestHalEnabled; @NonNull private final Context mContext; @@ -191,7 +193,7 @@ public FingerprintProvider(@NonNull Context context, } private String getTag() { - return "FingerprintProvider/" + mHalInstanceName; + return TAG + "/" + mHalInstanceName; } boolean hasHalInstance() { @@ -721,7 +723,7 @@ void setTestHalEnabled(boolean enabled) { // TODO(b/174868353): workaround for gaps in HAL interface (remove and get directly from HAL) // reads values via an overlay instead of querying the HAL @NonNull - private List getWorkaroundSensorProps(@NonNull Context context) { + public static List getWorkaroundSensorProps(@NonNull Context context) { final List sensorLocations = new ArrayList<>(); final TypedArray sfpsProps = context.getResources().obtainTypedArray( @@ -742,7 +744,7 @@ private List getWorkaroundSensorProps(@NonNull Context c } @Nullable - private SensorLocationInternal parseSensorLocation(@Nullable TypedArray array) { + private static SensorLocationInternal parseSensorLocation(@Nullable TypedArray array) { if (array == null) { return null; } @@ -754,7 +756,7 @@ private SensorLocationInternal parseSensorLocation(@Nullable TypedArray array) { array.getInt(2, 0), array.getInt(3, 0)); } catch (Exception e) { - Slog.w(getTag(), "malformed sensor location", e); + Slog.w(TAG, "malformed sensor location", e); } return null; } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java index c1a86386dfd4..ab2f366f5e07 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java @@ -128,6 +128,8 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider private final int mSensorId; private final boolean mIsPowerbuttonFps; + private boolean mCleanup; + private final class BiometricTaskStackListener extends TaskStackListener { @Override public void onTaskStackChanged() { @@ -349,6 +351,9 @@ public void onEnumerate(long deviceId, int fingerId, int groupId, int remaining) mCurrentUserId = UserHandle.USER_NULL; }); + mCleanup = context.getResources().getBoolean( + com.android.internal.R.bool.config_cleanupUnusedFingerprints); + try { ActivityManager.getService().registerUserSwitchObserver(mUserSwitchObserver, TAG); } catch (RemoteException e) { @@ -363,7 +368,7 @@ public static Fingerprint21 newInstance(@NonNull Context context, @NonNull LockoutResetDispatcher lockoutResetDispatcher, @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher) { final BiometricScheduler scheduler = - new BiometricScheduler(TAG, + new BiometricScheduler(context, TAG, BiometricScheduler.sensorTypeFromFingerprintProperties(sensorProps), gestureAvailabilityDispatcher); final HalResultController controller = new HalResultController(sensorProps.sensorId, @@ -734,6 +739,9 @@ mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), private void scheduleInternalCleanup(int userId, @Nullable ClientMonitorCallback callback) { + if (!mCleanup) { + return; + } mHandler.post(() -> { scheduleUpdateActiveUserWithoutHandler(userId); diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java index bea0f4ffd45d..9726ae3d5764 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java @@ -137,9 +137,9 @@ public class Fingerprint21UdfpsMock extends Fingerprint21 implements TrustManage private static class TestableBiometricScheduler extends BiometricScheduler { @NonNull private Fingerprint21UdfpsMock mFingerprint21; - TestableBiometricScheduler(@NonNull String tag, @NonNull Handler handler, + TestableBiometricScheduler(Context context, @NonNull String tag, @NonNull Handler handler, @Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher) { - super(tag, BiometricScheduler.SENSOR_TYPE_FP_OTHER, gestureAvailabilityDispatcher); + super(context, tag, BiometricScheduler.SENSOR_TYPE_FP_OTHER, gestureAvailabilityDispatcher); } void init(@NonNull Fingerprint21UdfpsMock fingerprint21) { @@ -254,7 +254,7 @@ public static Fingerprint21UdfpsMock newInstance(@NonNull Context context, final Handler handler = new Handler(Looper.getMainLooper()); final TestableBiometricScheduler scheduler = - new TestableBiometricScheduler(TAG, handler, gestureAvailabilityDispatcher); + new TestableBiometricScheduler(context, TAG, handler, gestureAvailabilityDispatcher); final MockHalResultController controller = new MockHalResultController(sensorProps.sensorId, context, handler, scheduler); return new Fingerprint21UdfpsMock(context, biometricStateCallback, sensorProps, scheduler, diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java index 7ed1a514f9bc..c5a9295fa4c2 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java @@ -59,6 +59,7 @@ class FingerprintAuthenticationClient extends AuthenticationClient mALSProbeCallback; @@ -83,6 +84,7 @@ class FingerprintAuthenticationClient extends AuthenticationClient token) { diff --git a/services/core/java/com/android/server/broadcastradio/hal2/Utils.java b/services/core/java/com/android/server/broadcastradio/hal2/Utils.java index 384c9bab94b4..85d796e8404d 100644 --- a/services/core/java/com/android/server/broadcastradio/hal2/Utils.java +++ b/services/core/java/com/android/server/broadcastradio/hal2/Utils.java @@ -25,7 +25,7 @@ enum FrequencyBand { AM_LW, AM_MW, AM_SW, -}; +} class Utils { private static final String TAG = "BcRadio2Srv.utils"; diff --git a/services/core/java/com/android/server/camera/CameraServiceProxy.java b/services/core/java/com/android/server/camera/CameraServiceProxy.java index aec60deba933..2b027a680d41 100644 --- a/services/core/java/com/android/server/camera/CameraServiceProxy.java +++ b/services/core/java/com/android/server/camera/CameraServiceProxy.java @@ -81,6 +81,8 @@ import com.android.server.SystemService; import com.android.server.wm.WindowManagerInternal; +import ink.kaleidoscope.server.ParallelSpaceManagerService; + import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; @@ -346,6 +348,7 @@ public void onReceive(Context context, Intent intent) { case Intent.ACTION_USER_INFO_CHANGED: case Intent.ACTION_MANAGED_PROFILE_ADDED: case Intent.ACTION_MANAGED_PROFILE_REMOVED: + case Intent.ACTION_PARALLEL_SPACE_CHANGED: synchronized(mLock) { // Return immediately if we haven't seen any users start yet if (mEnabledCameraUsers == null) return; @@ -694,6 +697,7 @@ public void onStart() { filter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED); filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED); filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED); + filter.addAction(Intent.ACTION_PARALLEL_SPACE_CHANGED); mContext.registerReceiver(mIntentReceiver, filter); publishBinderService(CAMERA_SERVICE_PROXY_BINDER_NAME, mCameraServiceProxy); @@ -937,6 +941,7 @@ private Set getEnabledUserHandles(int currentUserHandle) { for (int id : userProfiles) { handles.add(id); } + handles.addAll(ParallelSpaceManagerService.getCurrentParallelUserIds()); return handles; } diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java index 6f9a17682dd7..1cdb16291912 100644 --- a/services/core/java/com/android/server/clipboard/ClipboardService.java +++ b/services/core/java/com/android/server/clipboard/ClipboardService.java @@ -88,6 +88,8 @@ import com.android.server.uri.UriGrantsManagerInternal; import com.android.server.wm.WindowManagerInternal; +import ink.kaleidoscope.server.ParallelSpaceManagerService; + import java.util.HashSet; import java.util.List; import java.util.function.Consumer; @@ -647,6 +649,9 @@ private PerUserClipboard getClipboardLocked(@UserIdInt int userId) { } List getRelatedProfiles(@UserIdInt int userId) { + if (ParallelSpaceManagerService.isInteractive(userId)) + return ParallelSpaceManagerService.getInteractiveUsers(); + final List related; final long origId = Binder.clearCallingIdentity(); try { diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java index 73afa60e8510..eb81e70363d4 100644 --- a/services/core/java/com/android/server/content/SyncManager.java +++ b/services/core/java/com/android/server/content/SyncManager.java @@ -2215,7 +2215,8 @@ protected void dumpSyncState(PrintWriter pw, SyncAdapterStateFetcher buckets) { pw.print("Storage low: "); pw.println(storageLowIntent != null); pw.print("Clock valid: "); pw.println(mSyncStorageEngine.isClockValid()); - final AccountAndUser[] accounts = AccountManagerService.getSingleton().getAllAccounts(); + final AccountAndUser[] accounts = + AccountManagerService.getSingleton().getAllAccountsForSystemProcess(); pw.print("Accounts: "); if (accounts != INITIAL_ACCOUNTS_ARRAY) { @@ -3274,7 +3275,8 @@ private ActiveSyncContext findActiveSyncContextH(int jobId) { private void updateRunningAccountsH(EndPoint syncTargets) { synchronized (mAccountsLock) { AccountAndUser[] oldAccounts = mRunningAccounts; - mRunningAccounts = AccountManagerService.getSingleton().getRunningAccounts(); + mRunningAccounts = + AccountManagerService.getSingleton().getRunningAccountsForSystem(); if (Log.isLoggable(TAG, Log.VERBOSE)) { Slog.v(TAG, "Accounts list: "); for (AccountAndUser acc : mRunningAccounts) { @@ -3316,7 +3318,8 @@ private void updateRunningAccountsH(EndPoint syncTargets) { } // Cancel all jobs from non-existent accounts. - AccountAndUser[] allAccounts = AccountManagerService.getSingleton().getAllAccounts(); + AccountAndUser[] allAccounts = + AccountManagerService.getSingleton().getAllAccountsForSystemProcess(); List ops = getAllPendingSyncs(); for (int i = 0, opsSize = ops.size(); i < opsSize; i++) { SyncOperation op = ops.get(i); diff --git a/services/core/java/com/android/server/display/ColorFade.java b/services/core/java/com/android/server/display/ColorFade.java index cb04ddfd636d..2898216a4e0d 100644 --- a/services/core/java/com/android/server/display/ColorFade.java +++ b/services/core/java/com/android/server/display/ColorFade.java @@ -62,7 +62,7 @@ * that belongs to the {@link DisplayPowerController}. *

    */ -final class ColorFade { +final class ColorFade implements ScreenStateAnimator { private static final String TAG = "ColorFade"; private static final boolean DEBUG = false; diff --git a/services/core/java/com/android/server/display/DisplayDeviceRepository.java b/services/core/java/com/android/server/display/DisplayDeviceRepository.java index 4ac79901b9dc..693f210160af 100644 --- a/services/core/java/com/android/server/display/DisplayDeviceRepository.java +++ b/services/core/java/com/android/server/display/DisplayDeviceRepository.java @@ -170,11 +170,14 @@ private void handleDisplayDeviceChanged(DisplayDevice device) { Trace.beginSection("handleDisplayDeviceChanged"); } int diff = device.mDebugLastLoggedDeviceInfo.diff(info); - if (diff == DisplayDeviceInfo.DIFF_STATE) { - Slog.i(TAG, "Display device changed state: \"" + info.name - + "\", " + Display.stateToString(info.state)); - } else if (diff != 0) { - Slog.i(TAG, "Display device changed: " + info); + + if (DEBUG) { + if (diff == DisplayDeviceInfo.DIFF_STATE) { + Slog.i(TAG, "Display device changed state: \"" + info.name + + "\", " + Display.stateToString(info.state)); + } else if (diff != 0) { + Slog.i(TAG, "Display device changed: " + info); + } } if ((diff & DisplayDeviceInfo.DIFF_COLOR_MODE) != 0) { diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java index ac72b1725432..3c8db49b2fca 100644 --- a/services/core/java/com/android/server/display/DisplayModeDirector.java +++ b/services/core/java/com/android/server/display/DisplayModeDirector.java @@ -1212,7 +1212,7 @@ private void updateLowPowerModeSettingLocked() { private void updateRefreshRateSettingLocked() { final ContentResolver cr = mContext.getContentResolver(); float minRefreshRate = Settings.System.getFloatForUser(cr, - Settings.System.MIN_REFRESH_RATE, 0f, cr.getUserId()); + Settings.System.MIN_REFRESH_RATE, 60.0f, cr.getUserId()); float peakRefreshRate = Settings.System.getFloatForUser(cr, Settings.System.PEAK_REFRESH_RATE, mDefaultPeakRefreshRate, cr.getUserId()); updateRefreshRateSettingLocked(minRefreshRate, peakRefreshRate, mDefaultRefreshRate); diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index efd2e3414466..e17d18b38806 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -21,6 +21,7 @@ import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.ActivityManager; +import android.content.ContentResolver; import android.content.Context; import android.content.pm.ParceledListSlice; import android.content.res.Resources; @@ -479,6 +480,12 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call private DualRampAnimator mScreenBrightnessRampAnimator; private BrightnessSetting.BrightnessSettingListener mBrightnessSettingListener; + // Screen-off animation + private int mScreenOffAnimation; + static final int SCREEN_OFF_FADE = 0; + static final int SCREEN_OFF_CRT = 1; + static final int SCREEN_OFF_SCALE = 2; + // True if this DisplayPowerController has been stopped and should no longer be running. private boolean mStopped; @@ -891,9 +898,49 @@ private void sendUpdatePowerStateLocked() { } } + private int getScreenAnimationModeForDisplayState(int displayState) { + switch (mScreenOffAnimation) { + case SCREEN_OFF_FADE: + return ScreenStateAnimator.MODE_FADE; + case SCREEN_OFF_CRT: + if (displayState == Display.STATE_OFF) { + return ScreenStateAnimator.MODE_COOL_DOWN; + } else { + return USE_COLOR_FADE_ON_ANIMATION ? ScreenStateAnimator.MODE_WARM_UP + : ScreenStateAnimator.MODE_FADE; + } + case SCREEN_OFF_SCALE: + if (displayState == Display.STATE_OFF) { + return ScreenStateAnimator.MODE_SCALE_DOWN; + } else { + return ScreenStateAnimator.MODE_FADE; + } + default: + return ScreenStateAnimator.MODE_FADE; + } + } + private void initialize(int displayState) { - mPowerState = new DisplayPowerState(mBlanker, - mColorFadeEnabled ? new ColorFade(mDisplayId) : null, mDisplayId, displayState); + final ContentResolver cr = mContext.getContentResolver(); + final ContentObserver observer = new ContentObserver(mHandler) { + @Override + public void onChange(boolean selfChange, Uri uri) { + mScreenOffAnimation = Settings.System.getIntForUser(cr, + Settings.System.SCREEN_OFF_ANIMATION, + SCREEN_OFF_FADE, UserHandle.USER_CURRENT); + if (mPowerState != null) { + mPowerState.setScreenStateAnimator(mScreenOffAnimation); + } + } + }; + cr.registerContentObserver(Settings.System.getUriFor( + Settings.System.SCREEN_OFF_ANIMATION), + false, observer, UserHandle.USER_ALL); + mScreenOffAnimation = Settings.System.getIntForUser(cr, + Settings.System.SCREEN_OFF_ANIMATION, + SCREEN_OFF_FADE, UserHandle.USER_CURRENT); + + mPowerState = new DisplayPowerState(mBlanker, mScreenOffAnimation, mDisplayId, displayState); if (mColorFadeEnabled) { mColorFadeOnAnimator = ObjectAnimator.ofFloat( @@ -1489,10 +1536,20 @@ && isValidBrightnessValue(mPowerRequest.screenBrightnessOverride)) { slowChange = false; mAppliedDimming = false; } - // If low power mode is enabled, scale brightness by screenLowPowerBrightnessFactor - // as long as it is above the minimum threshold. + + // If low power mode is enabled and Smart Pixels Service is stopped, + // scale brightness by screenLowPowerBrightnessFactor + // as long as it is above the minimum threshold + final int mSmartPixelsEnable = Settings.System.getIntForUser( + mContext.getContentResolver(), Settings.System.SMART_PIXELS_ENABLE, + 0, UserHandle.USER_CURRENT); + final int mSmartPixelsOnPowerSave = Settings.System.getIntForUser( + mContext.getContentResolver(), Settings.System.SMART_PIXELS_ON_POWER_SAVE, + 0, UserHandle.USER_CURRENT); + if (mPowerRequest.lowPowerMode) { - if (brightnessState > PowerManager.BRIGHTNESS_MIN) { + if ((brightnessState > PowerManager.BRIGHTNESS_MIN) && + ((mSmartPixelsEnable == 0) || (mSmartPixelsOnPowerSave == 0))) { final float brightnessFactor = Math.min(mPowerRequest.screenLowPowerBrightnessFactor, 1); final float lowPowerBrightnessFloat = (brightnessState * brightnessFactor); @@ -2069,7 +2126,7 @@ private void animateScreenStateChange(int target, boolean performScreenOffTransi // Skip the screen off animation and add a black surface to hide the // contents of the screen. mPowerState.prepareColorFade(mContext, - mColorFadeFadesConfig ? ColorFade.MODE_FADE : ColorFade.MODE_WARM_UP); + getScreenAnimationModeForDisplayState(Display.STATE_ON)); if (mColorFadeOffAnimator != null) { mColorFadeOffAnimator.end(); } @@ -2102,9 +2159,7 @@ private void animateScreenStateChange(int target, boolean performScreenOffTransi if (mPowerState.getColorFadeLevel() == 1.0f) { mPowerState.dismissColorFade(); } else if (mPowerState.prepareColorFade(mContext, - mColorFadeFadesConfig ? - ColorFade.MODE_FADE : - ColorFade.MODE_WARM_UP)) { + getScreenAnimationModeForDisplayState(Display.STATE_ON))) { mColorFadeOnAnimator.start(); } else { mColorFadeOnAnimator.end(); @@ -2206,9 +2261,7 @@ private void animateScreenStateChange(int target, boolean performScreenOffTransi mPowerState.dismissColorFadeResources(); } else if (performScreenOffTransition && mPowerState.prepareColorFade(mContext, - mColorFadeFadesConfig ? - ColorFade.MODE_FADE : ColorFade.MODE_COOL_DOWN) - && mPowerState.getScreenState() != Display.STATE_OFF) { + getScreenAnimationModeForDisplayState(Display.STATE_OFF))) { // Perform the screen off animation. mColorFadeOffAnimator.start(); } else { diff --git a/services/core/java/com/android/server/display/DisplayPowerState.java b/services/core/java/com/android/server/display/DisplayPowerState.java index 2f22d33f552a..04fe9d0aad25 100644 --- a/services/core/java/com/android/server/display/DisplayPowerState.java +++ b/services/core/java/com/android/server/display/DisplayPowerState.java @@ -54,7 +54,6 @@ final class DisplayPowerState { private final Handler mHandler; private final Choreographer mChoreographer; private final DisplayBlanker mBlanker; - private final ColorFade mColorFade; private final PhotonicModulator mPhotonicModulator; private final int mDisplayId; @@ -73,12 +72,14 @@ final class DisplayPowerState { private volatile boolean mStopped; + private ScreenStateAnimator mColorFade; + DisplayPowerState( - DisplayBlanker blanker, ColorFade colorFade, int displayId, int displayState) { + DisplayBlanker blanker, int screenAnimatorMode, int displayId, int displayState) { mHandler = new Handler(true /*async*/); mChoreographer = Choreographer.getInstance(); mBlanker = blanker; - mColorFade = colorFade; + setScreenStateAnimator(screenAnimatorMode); mPhotonicModulator = new PhotonicModulator(); mPhotonicModulator.start(); mDisplayId = displayId; @@ -99,6 +100,17 @@ final class DisplayPowerState { mColorFadeReady = true; } + public void setScreenStateAnimator(int mode) { + if (mColorFade != null) { + mColorFade.dismiss(); + } + if (mode == DisplayPowerController.SCREEN_OFF_FADE) { + mColorFade = new ColorFade(Display.DEFAULT_DISPLAY); + } else { + mColorFade = new ElectronBeam(Display.DEFAULT_DISPLAY); + } + } + public static final FloatProperty COLOR_FADE_LEVEL = new FloatProperty("electronBeamLevel") { @Override diff --git a/services/core/java/com/android/server/display/ElectronBeam.java b/services/core/java/com/android/server/display/ElectronBeam.java new file mode 100644 index 000000000000..7fa06ab1eaa6 --- /dev/null +++ b/services/core/java/com/android/server/display/ElectronBeam.java @@ -0,0 +1,873 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * Copyright (C) 2018 The Dirty Unicorns Project + * Copyright (C) 2022 The Potato Open Sauce Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.display; + +import static com.android.server.wm.utils.RotationAnimationUtils.hasProtectedContent; + +import android.content.Context; +import android.graphics.PixelFormat; +import android.graphics.SurfaceTexture; +import android.hardware.display.DisplayManagerInternal.DisplayTransactionListener; +import android.hardware.display.DisplayManagerInternal; +import android.opengl.EGL14; +import android.opengl.EGLConfig; +import android.opengl.EGLContext; +import android.opengl.EGLDisplay; +import android.opengl.EGLSurface; +import android.opengl.GLES10; +import android.opengl.GLES11Ext; +import android.os.IBinder; +import android.os.Looper; +import android.util.Slog; +import android.view.Display; +import android.view.DisplayInfo; +import android.view.Surface.OutOfResourcesException; +import android.view.Surface; +import android.view.SurfaceControl.Transaction; +import android.view.SurfaceControl; +import android.view.SurfaceSession; + +import com.android.server.LocalServices; + +import java.io.PrintWriter; +import java.lang.Math; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.FloatBuffer; + + +/** + * Bzzzoooop! *crackle* + *

    + * Animates a screen transition from on to off or off to on by applying + * some GL transformations to a screenshot. + *

    + * This component must only be created or accessed by the {@link Looper} thread + * that belongs to the {@link DisplayPowerController}. + *

    + */ +final class ElectronBeam implements ScreenStateAnimator { + private static final String TAG = "ElectronBeam"; + + private static final boolean DEBUG = false; + + // The layer for the electron beam surface. + // This is currently hardcoded to be one layer above the boot animation. + private static final int ELECTRON_BEAM_LAYER = 0x40000001; + + // The relative proportion of the animation to spend performing + // the horizontal stretch effect. The remainder is spent performing + // the vertical stretch effect. + private static final float HSTRETCH_DURATION = 0.5f; + private static final float VSTRETCH_DURATION = 1.0f - HSTRETCH_DURATION; + + // The number of frames to draw when preparing the animation so that it will + // be ready to run smoothly. We use 3 frames because we are triple-buffered. + // See code for details. + private static final int DEJANK_FRAMES = 3; + + private static final int EGL_GL_COLORSPACE_KHR = 0x309D; + private static final int EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT = 0x3490; + private static final int EGL_PROTECTED_CONTENT_EXT = 0x32C0; + + // Set to true when the animation context has been fully prepared. + private boolean mPrepared; + private int mMode; + + private final DisplayManagerInternal mDisplayManagerInternal; + private int mDisplayId; + private int mDisplayLayerStack; // layer stack associated with primary display + private int mDisplayWidth; // real width, not rotated + private int mDisplayHeight; // real height, not rotated + private SurfaceSession mSurfaceSession; + private SurfaceControl mSurfaceControl; + private Surface mSurface; + private NaturalSurfaceLayout mSurfaceLayout; + private EGLDisplay mEglDisplay; + private EGLConfig mEglConfig; + private EGLContext mEglContext; + private EGLSurface mEglSurface; + private boolean mSurfaceVisible; + private float mSurfaceAlpha; + private boolean mLastWasWideColor; + private boolean mLastWasProtectedContent; + + // Texture names. We only use one texture, which contains the screenshot. + private final int[] mTexNames = new int[1]; + private boolean mTexNamesGenerated; + private final float mTexMatrix[] = new float[16]; + + // Vertex and corresponding texture coordinates. + // We have 4 2D vertices, so 8 elements. The vertices form a quad. + private final FloatBuffer mVertexBuffer = createNativeFloatBuffer(8); + private final FloatBuffer mTexCoordBuffer = createNativeFloatBuffer(8); + + private final Transaction mTransaction = new Transaction(); + + /** + * Animates an electron beam warming up. + */ + public static final int MODE_WARM_UP = 0; + + /** + * Animates an electron beam shutting off. + */ + public static final int MODE_COOL_DOWN = 1; + + /** + * Animates a simple dim layer to fade the contents of the screen in or out progressively. + */ + public static final int MODE_FADE = 2; + + /** + * Animates a scale down of the screen + */ + public static final int MODE_SCALE_DOWN = 3; + + + public ElectronBeam(int displayId) { + mDisplayId = displayId; + mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class); + } + + /** + * Warms up the electron beam in preparation for turning on or off. + * This method prepares a GL context, and captures a screen shot. + * + * @param mode The desired mode for the upcoming animation. + * @return True if the electron beam is ready, false if it is uncontrollable. + */ + public boolean prepare(Context context, int mode) { + if (DEBUG) { + Slog.d(TAG, "prepare: mode=" + mode); + } + + mMode = mode; + + // Get the display size and layer stack. + // This is not expected to change while the electron beam surface is showing. + DisplayInfo displayInfo = mDisplayManagerInternal.getDisplayInfo(mDisplayId); + mDisplayLayerStack = displayInfo.layerStack; + mDisplayWidth = displayInfo.getNaturalWidth(); + mDisplayHeight = displayInfo.getNaturalHeight(); + + final IBinder token = SurfaceControl.getInternalDisplayToken(); + if (token == null) { + Slog.e(TAG, + "Failed to take screenshot because internal display is disconnected"); + return false; + } + final boolean isWideColor = SurfaceControl.getDynamicDisplayInfo(token).activeColorMode + == Display.COLOR_MODE_DISPLAY_P3; + + // Set mPrepared here so if initialization fails, resources can be cleaned up. + mPrepared = true; + + final SurfaceControl.ScreenshotHardwareBuffer hardwareBuffer = captureScreen(); + if (hardwareBuffer == null) { + dismiss(); + return false; + } + + final boolean isProtected = hasProtectedContent(hardwareBuffer.getHardwareBuffer()); + if (!createSurfaceControl(hardwareBuffer.containsSecureLayers())) { + dismiss(); + return false; + } + + // MODE_FADE use ColorLayer to implement. + if (mMode == MODE_FADE) { + return true; + } + + if (!(createEglContext(isProtected) && createEglSurface(isProtected, isWideColor) + && setScreenshotTextureAndSetViewport(hardwareBuffer))) { + dismiss(); + return false; + } + + // Done. + mLastWasProtectedContent = isProtected; + mLastWasWideColor = isWideColor; + + // Dejanking optimization. + // Some GL drivers can introduce a lot of lag in the first few frames as they + // initialize their state and allocate graphics buffers for rendering. + // Work around this problem by rendering the first frame of the animation a few + // times. The rest of the animation should run smoothly thereafter. + // The frames we draw here aren't visible because we are essentially just + // painting the screenshot as-is. + if (mode == MODE_COOL_DOWN || mode == MODE_SCALE_DOWN) { + for (int i = 0; i < DEJANK_FRAMES; i++) { + draw(1.0f); + } + } + return true; + } + + /** + * Dismisses the electron beam animation surface and cleans up. + * + * To prevent stray photons from leaking out after the electron beam has been + * turned off, it is a good idea to defer dismissing the animation until the + * electron beam has been turned back on fully. + */ + public void dismiss() { + if (DEBUG) { + Slog.d(TAG, "dismiss"); + } + + destroyScreenshotTexture(); + destroyEglSurface(); + destroySurface(); + mPrepared = false; + } + + + /** + * Draws an animation frame showing the electron beam activated at the + * specified level. + * + * @param level The electron beam level. + * @return True if successful. + */ + public boolean draw(float level) { + if (DEBUG) { + Slog.d(TAG, "drawFrame: level=" + level); + } + + if (!mPrepared) { + return false; + } + + if (mMode == MODE_FADE) { + return showSurface(1.0f - level); + } + + if (!attachEglContext()) { + return false; + } + try { + // Clear frame to solid black. + GLES10.glClearColor(0.0f, 0.0f, 0.0f, 0.5f); + GLES10.glClear(GLES10.GL_COLOR_BUFFER_BIT); + + // Draw the frame. + if (mMode == MODE_WARM_UP || mMode == MODE_COOL_DOWN) { + if (level < HSTRETCH_DURATION) { + drawHStretch(1.0f - (level / HSTRETCH_DURATION)); + } else { + drawVStretch(1.0f - ((level - HSTRETCH_DURATION) / VSTRETCH_DURATION)); + } + } else if (mMode == MODE_SCALE_DOWN) { + drawScaled(level); + } + + if (checkGlErrors("drawFrame")) { + return false; + } + + EGL14.eglSwapBuffers(mEglDisplay, mEglSurface); + } finally { + detachEglContext(); + } + + return showSurface(1.0f); + } + + private void drawScaled(float scale) { + final float curvedScale = scurve(scale, 8.0f); + + // set blending, enable alpha operations + GLES10.glEnable(GLES10.GL_BLEND); + GLES10.glBlendFunc(GLES10.GL_SRC_ALPHA, GLES10.GL_ONE_MINUS_SRC_ALPHA); + + // bind vertex buffer + GLES10.glVertexPointer(2, GLES10.GL_FLOAT, 0, mVertexBuffer); + GLES10.glEnableClientState(GLES10.GL_VERTEX_ARRAY); + + // set-up texturing + GLES10.glDisable(GLES10.GL_TEXTURE_2D); + GLES10.glEnable(GLES11Ext.GL_TEXTURE_EXTERNAL_OES); + + // bind texture and set blending for drawing planes + GLES10.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, mTexNames[0]); + GLES10.glTexEnvx(GLES10.GL_TEXTURE_ENV, GLES10.GL_TEXTURE_ENV_MODE, + mMode == MODE_WARM_UP ? GLES10.GL_MODULATE : GLES10.GL_REPLACE); + GLES10.glTexParameterx(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, + GLES10.GL_TEXTURE_MAG_FILTER, GLES10.GL_LINEAR); + GLES10.glTexParameterx(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, + GLES10.GL_TEXTURE_MIN_FILTER, GLES10.GL_LINEAR); + GLES10.glTexParameterx(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, + GLES10.GL_TEXTURE_WRAP_S, GLES10.GL_CLAMP_TO_EDGE); + GLES10.glTexParameterx(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, + GLES10.GL_TEXTURE_WRAP_T, GLES10.GL_CLAMP_TO_EDGE); + GLES10.glEnable(GLES11Ext.GL_TEXTURE_EXTERNAL_OES); + GLES10.glTexCoordPointer(2, GLES10.GL_FLOAT, 0, mTexCoordBuffer); + GLES10.glEnableClientState(GLES10.GL_TEXTURE_COORD_ARRAY); + + // Draw the frame + setQuad(mVertexBuffer, mDisplayWidth / 2 * (1.0f - curvedScale), + mDisplayHeight / 2 * (1.0f - curvedScale), + mDisplayWidth * curvedScale, mDisplayHeight * curvedScale); + GLES10.glDrawArrays(GLES10.GL_TRIANGLE_FAN, 0, 4); + + // dim progressively, using previous vertexes + GLES10.glDisable(GLES11Ext.GL_TEXTURE_EXTERNAL_OES); + GLES10.glDisableClientState(GLES10.GL_TEXTURE_COORD_ARRAY); + GLES10.glColorMask(true, true, true, true); + GLES10.glColor4f(0.0f, 0.0f, 0.0f, 1.0f - curvedScale); + GLES10.glDrawArrays(GLES10.GL_TRIANGLE_FAN, 0, 4); + + // clean up after drawing planes + GLES10.glDisableClientState(GLES10.GL_VERTEX_ARRAY); + GLES10.glDisable(GLES10.GL_BLEND); + } + + /** + * Draws a frame where the content of the electron beam is collapsing inwards upon + * itself vertically with red / green / blue channels dispersing and eventually + * merging down to a single horizontal line. + * + * @param stretch The stretch factor. 0.0 is no collapse, 1.0 is full collapse. + */ + private void drawVStretch(float stretch) { + // compute interpolation scale factors for each color channel + final float ar = scurve(stretch, 7.5f); + final float ag = scurve(stretch, 8.0f); + final float ab = scurve(stretch, 8.5f); + if (DEBUG) { + Slog.d(TAG, "drawVStretch: stretch=" + stretch + + ", ar=" + ar + ", ag=" + ag + ", ab=" + ab); + } + + // set blending + GLES10.glBlendFunc(GLES10.GL_ONE, GLES10.GL_ONE); + GLES10.glEnable(GLES10.GL_BLEND); + + // bind vertex buffer + GLES10.glVertexPointer(2, GLES10.GL_FLOAT, 0, mVertexBuffer); + GLES10.glEnableClientState(GLES10.GL_VERTEX_ARRAY); + + // set-up texturing + GLES10.glDisable(GLES10.GL_TEXTURE_2D); + GLES10.glEnable(GLES11Ext.GL_TEXTURE_EXTERNAL_OES); + + // bind texture and set blending for drawing planes + GLES10.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, mTexNames[0]); + GLES10.glTexEnvx(GLES10.GL_TEXTURE_ENV, GLES10.GL_TEXTURE_ENV_MODE, + mMode == MODE_WARM_UP ? GLES10.GL_MODULATE : GLES10.GL_REPLACE); + GLES10.glTexParameterx(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, + GLES10.GL_TEXTURE_MAG_FILTER, GLES10.GL_LINEAR); + GLES10.glTexParameterx(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, + GLES10.GL_TEXTURE_MIN_FILTER, GLES10.GL_LINEAR); + GLES10.glTexParameterx(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, + GLES10.GL_TEXTURE_WRAP_S, GLES10.GL_CLAMP_TO_EDGE); + GLES10.glTexParameterx(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, + GLES10.GL_TEXTURE_WRAP_T, GLES10.GL_CLAMP_TO_EDGE); + GLES10.glEnable(GLES11Ext.GL_TEXTURE_EXTERNAL_OES); + GLES10.glTexCoordPointer(2, GLES10.GL_FLOAT, 0, mTexCoordBuffer); + GLES10.glEnableClientState(GLES10.GL_TEXTURE_COORD_ARRAY); + + // draw the red plane + setVStretchQuad(mVertexBuffer, mDisplayWidth, mDisplayHeight, ar); + GLES10.glColorMask(true, false, false, true); + GLES10.glDrawArrays(GLES10.GL_TRIANGLE_FAN, 0, 4); + + // draw the green plane + setVStretchQuad(mVertexBuffer, mDisplayWidth, mDisplayHeight, ag); + GLES10.glColorMask(false, true, false, true); + GLES10.glDrawArrays(GLES10.GL_TRIANGLE_FAN, 0, 4); + + // draw the blue plane + setVStretchQuad(mVertexBuffer, mDisplayWidth, mDisplayHeight, ab); + GLES10.glColorMask(false, false, true, true); + GLES10.glDrawArrays(GLES10.GL_TRIANGLE_FAN, 0, 4); + + // clean up after drawing planes + GLES10.glDisable(GLES11Ext.GL_TEXTURE_EXTERNAL_OES); + GLES10.glDisableClientState(GLES10.GL_TEXTURE_COORD_ARRAY); + GLES10.glColorMask(true, true, true, true); + + // draw the white highlight (we use the last vertices) + if (mMode == MODE_COOL_DOWN) { + GLES10.glColor4f(ag, ag, ag, 1.0f); + GLES10.glDrawArrays(GLES10.GL_TRIANGLE_FAN, 0, 4); + } + + // clean up + GLES10.glDisableClientState(GLES10.GL_VERTEX_ARRAY); + GLES10.glDisable(GLES10.GL_BLEND); + } + + /** + * Draws a frame where the electron beam has been stretched out into + * a thin white horizontal line that fades as it collapses inwards. + * + * @param stretch The stretch factor. 0.0 is maximum stretch / no fade, + * 1.0 is collapsed / maximum fade. + */ + private void drawHStretch(float stretch) { + // compute interpolation scale factor + final float ag = scurve(stretch, 8.0f); + if (DEBUG) { + Slog.d(TAG, "drawHStretch: stretch=" + stretch + ", ag=" + ag); + } + + if (stretch < 1.0f) { + // bind vertex buffer + GLES10.glVertexPointer(2, GLES10.GL_FLOAT, 0, mVertexBuffer); + GLES10.glEnableClientState(GLES10.GL_VERTEX_ARRAY); + + // draw narrow fading white line + setHStretchQuad(mVertexBuffer, mDisplayWidth, mDisplayHeight, ag); + GLES10.glColor4f(1.0f - ag*0.75f, 1.0f - ag*0.75f, 1.0f - ag*0.75f, 1.0f); + GLES10.glDrawArrays(GLES10.GL_TRIANGLE_FAN, 0, 4); + + // clean up + GLES10.glDisableClientState(GLES10.GL_VERTEX_ARRAY); + } + } + + private static void setVStretchQuad(FloatBuffer vtx, float dw, float dh, float a) { + final float w = dw + (dw * a); + final float h = dh - (dh * a); + final float x = (dw - w) * 0.5f; + final float y = (dh - h) * 0.5f; + setQuad(vtx, x, y, w, h); + } + + private static void setHStretchQuad(FloatBuffer vtx, float dw, float dh, float a) { + final float w = 2 * dw * (1.0f - a); + final float h = 1.0f; + final float x = (dw - w) * 0.5f; + final float y = (dh - h) * 0.5f; + setQuad(vtx, x, y, w, h); + } + + private static void setQuad(FloatBuffer vtx, float x, float y, float w, float h) { + if (DEBUG) { + Slog.d(TAG, "setQuad: x=" + x + ", y=" + y + ", w=" + w + ", h=" + h); + } + vtx.put(0, x); + vtx.put(1, y); + vtx.put(2, x); + vtx.put(3, y + h); + vtx.put(4, x + w); + vtx.put(5, y + h); + vtx.put(6, x + w); + vtx.put(7, y); + } + + private boolean setScreenshotTextureAndSetViewport( + SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer) { + if (!attachEglContext()) { + return false; + } + try { + if (!mTexNamesGenerated) { + GLES10.glGenTextures(1, mTexNames, 0); + if (checkGlErrors("glGenTextures")) { + return false; + } + mTexNamesGenerated = true; + } + + final SurfaceTexture st = new SurfaceTexture(mTexNames[0]); + final Surface s = new Surface(st); + try { + s.attachAndQueueBufferWithColorSpace(screenshotBuffer.getHardwareBuffer(), + screenshotBuffer.getColorSpace()); + + st.updateTexImage(); + st.getTransformMatrix(mTexMatrix); + } finally { + s.release(); + st.release(); + } + + // Set up texture coordinates for a quad. + // We might need to change this if the texture ends up being + // a different size from the display for some reason. + mTexCoordBuffer.put(0, 0f); mTexCoordBuffer.put(1, 0f); + mTexCoordBuffer.put(2, 0f); mTexCoordBuffer.put(3, 1f); + mTexCoordBuffer.put(4, 1f); mTexCoordBuffer.put(5, 1f); + mTexCoordBuffer.put(6, 1f); mTexCoordBuffer.put(7, 0f); + + // Set up our viewport. + GLES10.glViewport(0, 0, mDisplayWidth, mDisplayHeight); + GLES10.glMatrixMode(GLES10.GL_PROJECTION); + GLES10.glLoadIdentity(); + GLES10.glOrthof(0, mDisplayWidth, 0, mDisplayHeight, 0, 1); + GLES10.glMatrixMode(GLES10.GL_MODELVIEW); + GLES10.glLoadIdentity(); + GLES10.glMatrixMode(GLES10.GL_TEXTURE); + GLES10.glLoadIdentity(); + GLES10.glLoadMatrixf(mTexMatrix, 0); + } finally { + detachEglContext(); + } + return true; + } + + private void destroyScreenshotTexture() { + if (mTexNamesGenerated) { + mTexNamesGenerated = false; + if (attachEglContext()) { + try { + GLES10.glDeleteTextures(1, mTexNames, 0); + checkGlErrors("glDeleteTextures"); + } finally { + detachEglContext(); + } + } + } + } + + private SurfaceControl.ScreenshotHardwareBuffer captureScreen() { + SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer = + mDisplayManagerInternal.systemScreenshot(mDisplayId); + if (screenshotBuffer == null) { + Slog.e(TAG, "Failed to take screenshot. Buffer is null"); + return null; + } + return screenshotBuffer; + } + + private boolean createEglContext(boolean isProtected) { + if (mEglDisplay == null) { + mEglDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY); + if (mEglDisplay == EGL14.EGL_NO_DISPLAY) { + logEglError("eglGetDisplay"); + return false; + } + + int[] version = new int[2]; + if (!EGL14.eglInitialize(mEglDisplay, version, 0, version, 1)) { + mEglDisplay = null; + logEglError("eglInitialize"); + return false; + } + } + + if (mEglConfig == null) { + int[] eglConfigAttribList = new int[] { + EGL14.EGL_RED_SIZE, 8, + EGL14.EGL_GREEN_SIZE, 8, + EGL14.EGL_BLUE_SIZE, 8, + EGL14.EGL_ALPHA_SIZE, 8, + EGL14.EGL_NONE + }; + int[] numEglConfigs = new int[1]; + EGLConfig[] eglConfigs = new EGLConfig[1]; + if (!EGL14.eglChooseConfig(mEglDisplay, eglConfigAttribList, 0, + eglConfigs, 0, eglConfigs.length, numEglConfigs, 0)) { + logEglError("eglChooseConfig"); + return false; + } + + mEglConfig = eglConfigs[0]; + } + + // The old context needs to be destroyed if the protected flag has changed. The context will + // be recreated based on the protected flag + if (mEglContext != null && isProtected != mLastWasProtectedContent) { + EGL14.eglDestroyContext(mEglDisplay, mEglContext); + mEglContext = null; + } + + if (mEglContext == null) { + int[] eglContextAttribList = new int[] { + EGL14.EGL_NONE, EGL14.EGL_NONE, + EGL14.EGL_NONE + }; + if (isProtected) { + eglContextAttribList[2] = EGL_PROTECTED_CONTENT_EXT; + eglContextAttribList[3] = EGL14.EGL_TRUE; + } + mEglContext = EGL14.eglCreateContext(mEglDisplay, mEglConfig, EGL14.EGL_NO_CONTEXT, + eglContextAttribList, 0); + if (mEglContext == null) { + logEglError("eglCreateContext"); + return false; + } + } + return true; + } + + private boolean createSurfaceControl(boolean isSecure) { + if (mSurfaceControl != null) { + mTransaction.setSecure(mSurfaceControl, isSecure).apply(); + return true; + } + + if (mSurfaceSession == null) { + mSurfaceSession = new SurfaceSession(); + } + + if (mSurfaceControl == null) { + try { + int flags; + if (mMode == MODE_FADE) { + flags = SurfaceControl.FX_SURFACE_EFFECT | SurfaceControl.HIDDEN; + } else { + flags = SurfaceControl.OPAQUE | SurfaceControl.HIDDEN; + } + SurfaceControl.Builder builder = new SurfaceControl.Builder(mSurfaceSession); + builder.setFlags(flags) + .setFormat(PixelFormat.OPAQUE) + .setName("ElectronBeam") + .setBufferSize(mDisplayWidth, mDisplayHeight); + mSurfaceControl = builder.build(); + } catch (OutOfResourcesException ex) { + Slog.e(TAG, "Unable to create surface.", ex); + return false; + } + + mTransaction.setLayerStack(mSurfaceControl, mDisplayLayerStack); + mTransaction.setWindowCrop(mSurfaceControl, mDisplayWidth, mDisplayHeight); + mSurface = new Surface(); + mSurface.copyFrom(mSurfaceControl); + + mSurfaceLayout = new NaturalSurfaceLayout(mDisplayManagerInternal, + mDisplayId, mSurfaceControl); + mSurfaceLayout.onDisplayTransaction(mTransaction); + mTransaction.apply(); + } + return true; + } + + private boolean createEglSurface(boolean isProtected, boolean isWideColor) { + // The old surface needs to be destroyed if either the protected flag or wide color flag has + // changed. The surface will be recreated based on the new flags. + boolean didContentAttributesChange = + isProtected != mLastWasProtectedContent || isWideColor != mLastWasWideColor; + if (mEglSurface != null && didContentAttributesChange) { + EGL14.eglDestroySurface(mEglDisplay, mEglSurface); + mEglSurface = null; + } + + if (mEglSurface == null) { + int[] eglSurfaceAttribList = new int[] { + EGL14.EGL_NONE, + EGL14.EGL_NONE, + EGL14.EGL_NONE, + EGL14.EGL_NONE, + EGL14.EGL_NONE + }; + + int index = 0; + // If the current display is in wide color, then so is the screenshot. + if (isWideColor) { + eglSurfaceAttribList[index++] = EGL_GL_COLORSPACE_KHR; + eglSurfaceAttribList[index++] = EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT; + } + if (isProtected) { + eglSurfaceAttribList[index++] = EGL_PROTECTED_CONTENT_EXT; + eglSurfaceAttribList[index] = EGL14.EGL_TRUE; + } + // turn our SurfaceControl into a Surface + mEglSurface = EGL14.eglCreateWindowSurface(mEglDisplay, mEglConfig, mSurface, + eglSurfaceAttribList, 0); + if (mEglSurface == null) { + logEglError("eglCreateWindowSurface"); + return false; + } + } + return true; + } + + private void destroyEglSurface() { + if (mEglSurface != null) { + if (!EGL14.eglDestroySurface(mEglDisplay, mEglSurface)) { + logEglError("eglDestroySurface"); + } + mEglSurface = null; + } + } + + private void destroySurface() { + if (mSurfaceControl != null) { + mSurfaceLayout.dispose(); + mSurfaceLayout = null; + mTransaction.remove(mSurfaceControl).apply(); + mSurface.release(); + mSurfaceControl = null; + mSurfaceVisible = false; + mSurfaceAlpha = 0f; + } + } + + private boolean showSurface(float alpha) { + if (!mSurfaceVisible || mSurfaceAlpha != alpha) { + mTransaction.setLayer(mSurfaceControl, ELECTRON_BEAM_LAYER) + .setAlpha(mSurfaceControl, alpha) + .show(mSurfaceControl) + .apply(); + mSurfaceVisible = true; + mSurfaceAlpha = alpha; + } + return true; + } + + private boolean attachEglContext() { + if (mEglSurface == null) { + return false; + } + if (!EGL14.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) { + logEglError("eglMakeCurrent"); + return false; + } + return true; + } + + private void detachEglContext() { + if (mEglDisplay != null) { + EGL14.eglMakeCurrent(mEglDisplay, + EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_CONTEXT); + } + } + + /** + * Interpolates a value in the range 0 .. 1 along a sigmoid curve + * yielding a result in the range 0 .. 1 scaled such that: + * scurve(0) == 0, scurve(0.5) == 0.5, scurve(1) == 1. + */ + private static float scurve(float value, float s) { + // A basic sigmoid has the form y = 1.0f / FloatMap.exp(-x * s). + // Here we take the input datum and shift it by 0.5 so that the + // domain spans the range -0.5 .. 0.5 instead of 0 .. 1. + final float x = value - 0.5f; + + // Next apply the sigmoid function to the scaled value + // which produces a value in the range 0 .. 1 so we subtract + // 0.5 to get a value in the range -0.5 .. 0.5 instead. + final float y = sigmoid(x, s) - 0.5f; + + // To obtain the desired boundary conditions we need to scale + // the result so that it fills a range of -1 .. 1. + final float v = sigmoid(0.5f, s) - 0.5f; + + // And finally remap the value back to a range of 0 .. 1. + return y / v * 0.5f + 0.5f; + } + + private static float sigmoid(float x, float s) { + return 1.0f / (1.0f + (float)Math.exp(-x * s)); + } + + private static FloatBuffer createNativeFloatBuffer(int size) { + ByteBuffer bb = ByteBuffer.allocateDirect(size * 4); + bb.order(ByteOrder.nativeOrder()); + return bb.asFloatBuffer(); + } + + private static void logEglError(String func) { + Slog.e(TAG, func + " failed: error " + EGL14.eglGetError(), new Throwable()); + } + + private static boolean checkGlErrors(String func) { + return checkGlErrors(func, true); + } + + private static boolean checkGlErrors(String func, boolean log) { + boolean hadError = false; + int error; + while ((error = GLES10.glGetError()) != GLES10.GL_NO_ERROR) { + if (log) { + Slog.e(TAG, func + " failed: error " + error, new Throwable()); + } + hadError = true; + } + return hadError; + } + + public void dump(PrintWriter pw) { + pw.println(); + pw.println("Electron Beam State:"); + pw.println(" mPrepared=" + mPrepared); + pw.println(" mMode=" + mMode); + pw.println(" mDisplayLayerStack=" + mDisplayLayerStack); + pw.println(" mDisplayWidth=" + mDisplayWidth); + pw.println(" mDisplayHeight=" + mDisplayHeight); + pw.println(" mSurfaceVisible=" + mSurfaceVisible); + pw.println(" mSurfaceAlpha=" + mSurfaceAlpha); + } + + /** + * Keeps a surface aligned with the natural orientation of the device. + * Updates the position and transformation of the matrix whenever the display + * is rotated. This is a little tricky because the display transaction + * callback can be invoked on any thread, not necessarily the thread that + * owns the electron beam. + */ + private static final class NaturalSurfaceLayout implements DisplayTransactionListener { + private final DisplayManagerInternal mDisplayManagerInternal; + private final int mDisplayId; + private SurfaceControl mSurfaceControl; + + public NaturalSurfaceLayout(DisplayManagerInternal displayManagerInternal, + int displayId, SurfaceControl surfaceControl) { + mDisplayManagerInternal = displayManagerInternal; + mDisplayId = displayId; + mSurfaceControl = surfaceControl; + mDisplayManagerInternal.registerDisplayTransactionListener(this); + } + + public void dispose() { + synchronized (this) { + mSurfaceControl = null; + } + mDisplayManagerInternal.unregisterDisplayTransactionListener(this); + } + + @Override + public void onDisplayTransaction(Transaction t) { + synchronized (this) { + if (mSurfaceControl == null) { + return; + } + try { + DisplayInfo displayInfo = mDisplayManagerInternal.getDisplayInfo(mDisplayId); + switch (displayInfo.rotation) { + case Surface.ROTATION_0: + t.setPosition(mSurfaceControl, 0, 0); + t.setMatrix(mSurfaceControl, 1, 0, 0, 1); + break; + case Surface.ROTATION_90: + t.setPosition(mSurfaceControl, 0, displayInfo.logicalHeight); + t.setMatrix(mSurfaceControl, 0, -1, 1, 0); + break; + case Surface.ROTATION_180: + t.setPosition(mSurfaceControl, displayInfo.logicalWidth, + displayInfo.logicalHeight); + t.setMatrix(mSurfaceControl, -1, 0, 0, -1); + break; + case Surface.ROTATION_270: + t.setPosition(mSurfaceControl, displayInfo.logicalWidth, 0); + t.setMatrix(mSurfaceControl, 0, 1, -1, 0); + break; + } + } catch (Exception e) { + + } + } + } + } +} diff --git a/services/core/java/com/android/server/display/ScreenStateAnimator.java b/services/core/java/com/android/server/display/ScreenStateAnimator.java new file mode 100644 index 000000000000..226c09484369 --- /dev/null +++ b/services/core/java/com/android/server/display/ScreenStateAnimator.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2018 The Dirty Unicorns Project + * Copyright (C) 2022 The Potato Open Sauce Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.display; + +import java.io.PrintWriter; + +import android.content.Context; + +/** @hide */ +public interface ScreenStateAnimator { + public static final int MODE_WARM_UP = 0; + public static final int MODE_COOL_DOWN = 1; + public static final int MODE_FADE = 2; + public static final int MODE_SCALE_DOWN = 3; + + public boolean prepare(Context context, int mode); + + public default void dismissResources() {} + + public void dismiss(); + + public boolean draw(float level); + + public void dump(PrintWriter pw); +} diff --git a/services/core/java/com/android/server/display/color/ColorDisplayService.java b/services/core/java/com/android/server/display/color/ColorDisplayService.java index 017b96cc5f67..751c0ed3cb2a 100644 --- a/services/core/java/com/android/server/display/color/ColorDisplayService.java +++ b/services/core/java/com/android/server/display/color/ColorDisplayService.java @@ -381,8 +381,10 @@ public void onChange(boolean selfChange, Uri uri) { false /* notifyForDescendants */, mContentObserver, mCurrentUser); cr.registerContentObserver(System.getUriFor(System.DISPLAY_COLOR_MODE), false /* notifyForDescendants */, mContentObserver, mCurrentUser); - cr.registerContentObserver(Secure.getUriFor(Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED), - false /* notifyForDescendants */, mContentObserver, mCurrentUser); + if (isAccessibilityInversionAvailable()) { + cr.registerContentObserver(Secure.getUriFor(Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED), + false /* notifyForDescendants */, mContentObserver, mCurrentUser); + } cr.registerContentObserver( Secure.getUriFor(Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED), false /* notifyForDescendants */, mContentObserver, mCurrentUser); @@ -570,7 +572,13 @@ private boolean isAccessiblityDaltonizerEnabled() { private boolean isAccessiblityInversionEnabled() { return Secure.getIntForUser(getContext().getContentResolver(), - Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED, 0, mCurrentUser) != 0; + Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED, 0, mCurrentUser) != 0 + && isAccessibilityInversionAvailable(); + } + + private boolean isAccessibilityInversionAvailable() { + return getContext().getResources().getBoolean( + com.android.internal.R.bool.config_displayInversionAvailable); } private boolean isAccessibilityEnabled() { diff --git a/services/core/java/com/android/server/hdmi/HdmiUtils.java b/services/core/java/com/android/server/hdmi/HdmiUtils.java index ba19cf062cdf..308a0c801076 100644 --- a/services/core/java/com/android/server/hdmi/HdmiUtils.java +++ b/services/core/java/com/android/server/hdmi/HdmiUtils.java @@ -20,6 +20,8 @@ import static com.android.server.hdmi.Constants.ADDR_BACKUP_2; import static com.android.server.hdmi.Constants.ADDR_TV; +import static java.util.Map.entry; + import android.annotation.Nullable; import android.hardware.hdmi.HdmiControlManager; import android.hardware.hdmi.HdmiDeviceInfo; @@ -45,7 +47,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; @@ -57,38 +58,34 @@ final class HdmiUtils { private static final String TAG = "HdmiUtils"; - private static final Map> ADDRESS_TO_TYPE = - new HashMap>() { - { - put(Constants.ADDR_TV, Lists.newArrayList(HdmiDeviceInfo.DEVICE_TV)); - put(Constants.ADDR_RECORDER_1, - Lists.newArrayList(HdmiDeviceInfo.DEVICE_RECORDER)); - put(Constants.ADDR_RECORDER_2, - Lists.newArrayList(HdmiDeviceInfo.DEVICE_RECORDER)); - put(Constants.ADDR_TUNER_1, Lists.newArrayList(HdmiDeviceInfo.DEVICE_TUNER)); - put(Constants.ADDR_PLAYBACK_1, - Lists.newArrayList(HdmiDeviceInfo.DEVICE_PLAYBACK)); - put(Constants.ADDR_AUDIO_SYSTEM, - Lists.newArrayList(HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM)); - put(Constants.ADDR_TUNER_2, Lists.newArrayList(HdmiDeviceInfo.DEVICE_TUNER)); - put(Constants.ADDR_TUNER_3, Lists.newArrayList(HdmiDeviceInfo.DEVICE_TUNER)); - put(Constants.ADDR_PLAYBACK_2, - Lists.newArrayList(HdmiDeviceInfo.DEVICE_PLAYBACK)); - put(Constants.ADDR_RECORDER_3, - Lists.newArrayList(HdmiDeviceInfo.DEVICE_RECORDER)); - put(Constants.ADDR_TUNER_4, Lists.newArrayList(HdmiDeviceInfo.DEVICE_TUNER)); - put(Constants.ADDR_PLAYBACK_3, - Lists.newArrayList(HdmiDeviceInfo.DEVICE_PLAYBACK)); - put(Constants.ADDR_BACKUP_1, Lists.newArrayList(HdmiDeviceInfo.DEVICE_PLAYBACK, - HdmiDeviceInfo.DEVICE_RECORDER, HdmiDeviceInfo.DEVICE_TUNER, - HdmiDeviceInfo.DEVICE_VIDEO_PROCESSOR)); - put(Constants.ADDR_BACKUP_2, Lists.newArrayList(HdmiDeviceInfo.DEVICE_PLAYBACK, - HdmiDeviceInfo.DEVICE_RECORDER, HdmiDeviceInfo.DEVICE_TUNER, - HdmiDeviceInfo.DEVICE_VIDEO_PROCESSOR)); - put(Constants.ADDR_SPECIFIC_USE, Lists.newArrayList(ADDR_TV)); - put(Constants.ADDR_UNREGISTERED, Collections.emptyList()); - } - }; + private static final Map> ADDRESS_TO_TYPE = Map.ofEntries( + entry(Constants.ADDR_TV, Lists.newArrayList(HdmiDeviceInfo.DEVICE_TV)), + entry(Constants.ADDR_RECORDER_1, + Lists.newArrayList(HdmiDeviceInfo.DEVICE_RECORDER)), + entry(Constants.ADDR_RECORDER_2, + Lists.newArrayList(HdmiDeviceInfo.DEVICE_RECORDER)), + entry(Constants.ADDR_TUNER_1, Lists.newArrayList(HdmiDeviceInfo.DEVICE_TUNER)), + entry(Constants.ADDR_PLAYBACK_1, + Lists.newArrayList(HdmiDeviceInfo.DEVICE_PLAYBACK)), + entry(Constants.ADDR_AUDIO_SYSTEM, + Lists.newArrayList(HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM)), + entry(Constants.ADDR_TUNER_2, Lists.newArrayList(HdmiDeviceInfo.DEVICE_TUNER)), + entry(Constants.ADDR_TUNER_3, Lists.newArrayList(HdmiDeviceInfo.DEVICE_TUNER)), + entry(Constants.ADDR_PLAYBACK_2, + Lists.newArrayList(HdmiDeviceInfo.DEVICE_PLAYBACK)), + entry(Constants.ADDR_RECORDER_3, + Lists.newArrayList(HdmiDeviceInfo.DEVICE_RECORDER)), + entry(Constants.ADDR_TUNER_4, Lists.newArrayList(HdmiDeviceInfo.DEVICE_TUNER)), + entry(Constants.ADDR_PLAYBACK_3, + Lists.newArrayList(HdmiDeviceInfo.DEVICE_PLAYBACK)), + entry(Constants.ADDR_BACKUP_1, Lists.newArrayList(HdmiDeviceInfo.DEVICE_PLAYBACK, + HdmiDeviceInfo.DEVICE_RECORDER, HdmiDeviceInfo.DEVICE_TUNER, + HdmiDeviceInfo.DEVICE_VIDEO_PROCESSOR)), + entry(Constants.ADDR_BACKUP_2, Lists.newArrayList(HdmiDeviceInfo.DEVICE_PLAYBACK, + HdmiDeviceInfo.DEVICE_RECORDER, HdmiDeviceInfo.DEVICE_TUNER, + HdmiDeviceInfo.DEVICE_VIDEO_PROCESSOR)), + entry(Constants.ADDR_SPECIFIC_USE, Lists.newArrayList(ADDR_TV)), + entry(Constants.ADDR_UNREGISTERED, Collections.emptyList())); private static final String[] DEFAULT_NAMES = { "TV", diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index 72612a0468cd..a9d747cc61e4 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -1799,8 +1799,8 @@ public boolean transferTouchFocus(@NonNull InputChannel fromChannel, */ public boolean transferTouchFocus(@NonNull IBinder fromChannelToken, @NonNull IBinder toChannelToken) { - Objects.nonNull(fromChannelToken); - Objects.nonNull(toChannelToken); + Objects.requireNonNull(fromChannelToken); + Objects.requireNonNull(toChannelToken); return mNative.transferTouchFocus(fromChannelToken, toChannelToken, false /* isDragDrop */); } diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index faa219e89aa9..5e4acd451545 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -197,6 +197,7 @@ import com.android.server.wm.WindowManagerInternal; import com.google.android.collect.Sets; +import ink.kaleidoscope.server.ParallelSpaceManagerService; import java.io.FileDescriptor; import java.io.IOException; @@ -2056,6 +2057,10 @@ private boolean calledFromValidUserLocked() { if (userId == mSettings.getCurrentUserId()) { return true; } + if (ParallelSpaceManagerService.convertToParallelOwnerIfPossible(userId) + == mSettings.getCurrentUserId()) { + return true; + } // Caveat: A process which has INTERACT_ACROSS_USERS_FULL gets results for the // foreground user, not for the user of that process. Accordingly InputMethodManagerService @@ -3854,7 +3859,9 @@ private InputBindResult startInputOrWindowGainedFocusInternal( return InputBindResult.INVALID_USER; } } else { - userId = callingUserId; + // For parallel users, always use IM from owners. + userId = ParallelSpaceManagerService + .convertToParallelOwnerIfPossible(callingUserId); } final InputBindResult result; synchronized (ImfLock.class) { diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java b/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java index c199bb30a6d3..2d3f176f5d4d 100644 --- a/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java +++ b/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java @@ -519,9 +519,9 @@ private int toStatsTransactionResult(@ContextHubTransaction.Result int result) { @Override public String toString() { StringBuilder sb = new StringBuilder(100); - TransactionRecord[] arr; + ContextHubServiceTransaction[] arr; synchronized (this) { - arr = mTransactionQueue.toArray(new TransactionRecord[0]); + arr = mTransactionQueue.toArray(new ContextHubServiceTransaction[0]); } for (int i = 0; i < arr.length; i++) { sb.append(i + ": " + arr[i] + "\n"); diff --git a/services/core/java/com/android/server/location/gnss/GnssConfiguration.java b/services/core/java/com/android/server/location/gnss/GnssConfiguration.java index 1435016fc55a..4406d52eee24 100644 --- a/services/core/java/com/android/server/location/gnss/GnssConfiguration.java +++ b/services/core/java/com/android/server/location/gnss/GnssConfiguration.java @@ -286,26 +286,24 @@ void reloadGpsProperties(boolean inEmergency, int activeSubId) { Log.e(TAG, "Unable to set " + CONFIG_ES_EXTENSION_SEC + ": " + mEsExtensionSec); } - Map map = new HashMap() { - { - put(CONFIG_SUPL_VER, GnssConfiguration::native_set_supl_version); - put(CONFIG_SUPL_MODE, GnssConfiguration::native_set_supl_mode); + Map map = new HashMap(); - if (isConfigSuplEsSupported(gnssConfigurationIfaceVersion)) { - put(CONFIG_SUPL_ES, GnssConfiguration::native_set_supl_es); - } + map.put(CONFIG_SUPL_VER, GnssConfiguration::native_set_supl_version); + map.put(CONFIG_SUPL_MODE, GnssConfiguration::native_set_supl_mode); - put(CONFIG_LPP_PROFILE, GnssConfiguration::native_set_lpp_profile); - put(CONFIG_A_GLONASS_POS_PROTOCOL_SELECT, - GnssConfiguration::native_set_gnss_pos_protocol_select); - put(CONFIG_USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL, - GnssConfiguration::native_set_emergency_supl_pdn); + if (isConfigSuplEsSupported(gnssConfigurationIfaceVersion)) { + map.put(CONFIG_SUPL_ES, GnssConfiguration::native_set_supl_es); + } - if (isConfigGpsLockSupported(gnssConfigurationIfaceVersion)) { - put(CONFIG_GPS_LOCK, GnssConfiguration::native_set_gps_lock); - } - } - }; + map.put(CONFIG_LPP_PROFILE, GnssConfiguration::native_set_lpp_profile); + map.put(CONFIG_A_GLONASS_POS_PROTOCOL_SELECT, + GnssConfiguration::native_set_gnss_pos_protocol_select); + map.put(CONFIG_USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL, + GnssConfiguration::native_set_emergency_supl_pdn); + + if (isConfigGpsLockSupported(gnssConfigurationIfaceVersion)) { + map.put(CONFIG_GPS_LOCK, GnssConfiguration::native_set_gps_lock); + } for (Entry entry : map.entrySet()) { String propertyName = entry.getKey(); diff --git a/services/core/java/com/android/server/location/injector/SystemUserInfoHelper.java b/services/core/java/com/android/server/location/injector/SystemUserInfoHelper.java index ed1e65457b24..1a10387ff1a8 100644 --- a/services/core/java/com/android/server/location/injector/SystemUserInfoHelper.java +++ b/services/core/java/com/android/server/location/injector/SystemUserInfoHelper.java @@ -34,7 +34,11 @@ import com.android.internal.util.Preconditions; import com.android.server.LocalServices; +import ink.kaleidoscope.server.ParallelSpaceManagerService; + import java.io.FileDescriptor; +import java.lang.Integer; +import java.util.ArrayList; import java.util.Arrays; /** @@ -111,7 +115,8 @@ public boolean isCurrentUserId(@UserIdInt int userId) { if (activityManagerInternal != null) { final long identity = Binder.clearCallingIdentity(); try { - return activityManagerInternal.isCurrentProfile(userId); + return activityManagerInternal.isCurrentProfile(userId) || + ParallelSpaceManagerService.isCurrentParallelUser(userId); } finally { Binder.restoreCallingIdentity(identity); } @@ -144,7 +149,10 @@ protected int[] getProfileIds(@UserIdInt int userId) { final long identity = Binder.clearCallingIdentity(); try { - return userManager.getEnabledProfileIds(userId); + ArrayList profiles = new ArrayList<>( + Arrays.asList(userManager.getEnabledProfileIds(userId))); + profiles.addAll(ParallelSpaceManagerService.getCurrentParallelUserIds()); + return profiles.stream().mapToInt(i -> ((Integer) i).intValue()).toArray(); } finally { Binder.restoreCallingIdentity(identity); } diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java index 78cffa6f4f79..1ce15822b910 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsService.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java @@ -138,6 +138,7 @@ import com.android.internal.widget.LockscreenCredential; import com.android.internal.widget.RebootEscrowListener; import com.android.internal.widget.VerifyCredentialResponse; +import com.android.server.app.AppLockManagerServiceInternal; import com.android.server.LocalServices; import com.android.server.ServiceThread; import com.android.server.SystemService; @@ -2469,6 +2470,7 @@ private void notifyPasswordChanged(LockscreenCredential newCredential, @UserIdIn PasswordMetrics.computeForCredential(newCredential), userId); LocalServices.getService(WindowManagerInternal.class).reportPasswordChanged(userId); + LocalServices.getService(AppLockManagerServiceInternal.class).reportPasswordChanged(userId); }); } diff --git a/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java b/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java index 6759d79eedca..9a190316f4eb 100644 --- a/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java +++ b/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java @@ -32,7 +32,6 @@ import android.os.PowerWhitelistManager; import android.os.UserHandle; import android.text.TextUtils; -import android.util.EventLog; import android.util.Log; import android.view.KeyEvent; @@ -118,12 +117,6 @@ public static MediaButtonReceiverHolder create(Context context, int userId, int componentType = getComponentType(pendingIntent); ComponentName componentName = getComponentName(pendingIntent, componentType); if (componentName != null) { - if (!TextUtils.equals(componentName.getPackageName(), sessionPackageName)) { - EventLog.writeEvent(0x534e4554, "238177121", -1, ""); // SafetyNet logging - throw new IllegalArgumentException("ComponentName does not belong to " - + "sessionPackageName. sessionPackageName = " + sessionPackageName - + ", ComponentName pkg = " + componentName.getPackageName()); - } return new MediaButtonReceiverHolder(userId, pendingIntent, componentName, componentType); } diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java index b8131a8ee5b5..604e8f3949f4 100644 --- a/services/core/java/com/android/server/media/MediaSessionRecord.java +++ b/services/core/java/com/android/server/media/MediaSessionRecord.java @@ -52,8 +52,6 @@ import android.os.RemoteException; import android.os.ResultReceiver; import android.os.SystemClock; -import android.text.TextUtils; -import android.util.EventLog; import android.util.Log; import android.view.KeyEvent; @@ -940,14 +938,6 @@ public void setFlags(int flags) throws RemoteException { @Override public void setMediaButtonReceiver(PendingIntent pi, String sessionPackageName) throws RemoteException { - //mPackageName has been verified in MediaSessionService.enforcePackageName(). - if (!TextUtils.equals(sessionPackageName, mPackageName)) { - EventLog.writeEvent(0x534e4554, "238177121", -1, ""); // SafetyNet logging - throw new IllegalArgumentException("sessionPackageName name does not match " - + "package name provided to MediaSessionRecord. sessionPackageName = " - + sessionPackageName + ", pkg = " - + mPackageName); - } final long token = Binder.clearCallingIdentity(); try { if ((mPolicies & MediaSessionPolicyProvider.SESSION_POLICY_IGNORE_BUTTON_RECEIVER) @@ -966,15 +956,6 @@ public void setMediaButtonReceiver(PendingIntent pi, String sessionPackageName) public void setMediaButtonBroadcastReceiver(ComponentName receiver) throws RemoteException { final long token = Binder.clearCallingIdentity(); try { - //mPackageName has been verified in MediaSessionService.enforcePackageName(). - if (receiver != null && !TextUtils.equals( - mPackageName, receiver.getPackageName())) { - EventLog.writeEvent(0x534e4554, "238177121", -1, ""); // SafetyNet logging - throw new IllegalArgumentException("receiver does not belong to " - + "package name provided to MediaSessionRecord. Pkg = " + mPackageName - + ", Receiver Pkg = " + receiver.getPackageName()); - } - if ((mPolicies & MediaSessionPolicyProvider.SESSION_POLICY_IGNORE_BUTTON_RECEIVER) != 0) { return; diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java index b89147ea181a..c2e937142a29 100644 --- a/services/core/java/com/android/server/media/MediaSessionService.java +++ b/services/core/java/com/android/server/media/MediaSessionService.java @@ -41,6 +41,7 @@ import android.content.IntentFilter; import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; +import android.database.ContentObserver; import android.media.AudioManager; import android.media.AudioPlaybackConfiguration; import android.media.AudioSystem; @@ -59,6 +60,7 @@ import android.media.session.MediaController; import android.media.session.MediaSession; import android.media.session.MediaSessionManager; +import android.net.Uri; import android.os.Binder; import android.os.Bundle; import android.os.Handler; @@ -928,10 +930,16 @@ final class FullUserRecord implements MediaSessionStack.OnMediaButtonSessionChan private IOnMediaKeyListener mOnMediaKeyListener; private int mOnMediaKeyListenerUid; + private boolean mAdaptivePlaybackEnabled; + FullUserRecord(int fullUserId) { mFullUserId = fullUserId; mContentResolver = mContext.createContextAsUser(UserHandle.of(mFullUserId), 0) .getContentResolver(); + SettingsObserver settingsObserver = new SettingsObserver(); + settingsObserver.observe(); + mAdaptivePlaybackEnabled = Settings.System.getInt(mContentResolver, + Settings.System.ADAPTIVE_PLAYBACK_ENABLED, 0) == 1; mPriorityStack = new MediaSessionStack(mAudioPlayerStateMonitor, this); // Restore the remembered media button receiver before the boot. String mediaButtonReceiverInfo = Settings.Secure.getString(mContentResolver, @@ -1130,6 +1138,28 @@ public void binderDied() { } } } + + final class SettingsObserver extends ContentObserver { + private final Uri ADAPTIVE_PLAYBACK_ENABLED_URI = + Settings.System.getUriFor(Settings.System.ADAPTIVE_PLAYBACK_ENABLED); + + public SettingsObserver() { + super(null); + } + + private void observe() { + mContentResolver.registerContentObserver(ADAPTIVE_PLAYBACK_ENABLED_URI, false, + this); + } + + @Override + public void onChange(boolean selfChange, @Nullable Uri uri) { + if (ADAPTIVE_PLAYBACK_ENABLED_URI.equals(uri)) { + mAdaptivePlaybackEnabled = Settings.System.getInt(mContentResolver, + Settings.System.ADAPTIVE_PLAYBACK_ENABLED, 0) == 1; + } + } + } } final class SessionsListenerRecord implements IBinder.DeathRecipient { @@ -2213,7 +2243,9 @@ private void dispatchAdjustVolumeLocked(String packageName, String opPackageName + ". flags=" + flags + ", preferSuggestedStream=" + preferSuggestedStream + ", session=" + session); } - if (musicOnly && !AudioSystem.isStreamActive(AudioManager.STREAM_MUSIC, 0)) { + if (musicOnly && !mCurrentFullUserRecord.mAdaptivePlaybackEnabled + && direction != AudioManager.ADJUST_RAISE + && !AudioSystem.isStreamActive(AudioManager.STREAM_MUSIC, 0)) { if (DEBUG_KEY_EVENT) { Log.d(TAG, "Nothing is playing on the music stream. Skipping volume event," + " flags=" + flags); diff --git a/services/core/java/com/android/server/notification/BubbleExtractor.java b/services/core/java/com/android/server/notification/BubbleExtractor.java index a561390ac7e4..9a75bff9de54 100644 --- a/services/core/java/com/android/server/notification/BubbleExtractor.java +++ b/services/core/java/com/android/server/notification/BubbleExtractor.java @@ -90,7 +90,8 @@ public RankingReconsideration process(NotificationRecord record) { NotificationChannel recordChannel = record.getChannel(); if (!userEnabledBubbles || appPreference == BUBBLE_PREFERENCE_NONE - || !notifCanPresentAsBubble) { + || !notifCanPresentAsBubble + || record.getSbn().getIsContentSecure()) { record.setAllowBubble(false); if (!notifCanPresentAsBubble) { // clear out bubble metadata since it can't be used @@ -144,10 +145,9 @@ public void setActivityManager(ActivityManager manager) { */ @VisibleForTesting boolean canPresentAsBubble(NotificationRecord r) { - if (!mSupportsBubble) { + if (!mSupportsBubble || r.isBubbleUpSuppressedByAppLock()) { return false; } - Notification notification = r.getNotification(); Notification.BubbleMetadata metadata = notification.getBubbleMetadata(); String pkg = r.getSbn().getPackageName(); diff --git a/services/core/java/com/android/server/notification/NotificationManagerInternal.java b/services/core/java/com/android/server/notification/NotificationManagerInternal.java index bc3885605a6c..f064570334b9 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerInternal.java +++ b/services/core/java/com/android/server/notification/NotificationManagerInternal.java @@ -47,4 +47,7 @@ void cancelNotification(String pkg, String basePkg, int callingUid, int callingP void sendReviewPermissionsNotification(); void cleanupHistoryFiles(); + + void updateSecureNotifications(String pkg, boolean isContentSecure, + boolean isBubbleUpSuppressed, int userId); } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index f11801f61d58..832977d4349e 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -36,6 +36,7 @@ import static android.app.NotificationManager.ACTION_NOTIFICATION_LISTENER_ENABLED_CHANGED; import static android.app.NotificationManager.ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED; import static android.app.NotificationManager.BUBBLE_PREFERENCE_ALL; +import static android.app.NotificationManager.BUBBLE_PREFERENCE_NONE; import static android.app.NotificationManager.EXTRA_AUTOMATIC_ZEN_RULE_ID; import static android.app.NotificationManager.EXTRA_AUTOMATIC_ZEN_RULE_STATUS; import static android.app.NotificationManager.IMPORTANCE_DEFAULT; @@ -295,6 +296,7 @@ import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.UiThread; +import com.android.server.app.AppLockManagerServiceInternal; import com.android.server.lights.LightsManager; import com.android.server.lights.LogicalLight; import com.android.server.notification.ManagedServices.ManagedServiceInfo; @@ -570,6 +572,8 @@ public class NotificationManagerService extends SystemService { protected boolean mInCallStateOffHook = false; boolean mNotificationPulseEnabled; + private boolean mSoundVibScreenOn; + private Uri mInCallNotificationUri; private AudioAttributes mInCallNotificationAudioAttributes; private float mInCallNotificationVolume; @@ -674,6 +678,8 @@ public class NotificationManagerService extends SystemService { // Broadcast intent receiver for notification permissions review-related intents private ReviewNotificationPermissionsReceiver mReviewNotificationPermissionsReceiver; + private AppLockManagerServiceInternal mAppLockManagerService = null; + static class Archive { final SparseArray mEnabled; final int mBufferSize; @@ -1887,6 +1893,8 @@ private final class SettingsObserver extends ContentObserver { Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS); private final Uri LOCK_SCREEN_SHOW_NOTIFICATIONS = Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS); + private final Uri NOTIFICATION_SOUND_VIB_SCREEN_ON + = Settings.System.getUriFor(Settings.System.NOTIFICATION_SOUND_VIB_SCREEN_ON); SettingsObserver(Handler handler) { super(handler); @@ -1911,6 +1919,8 @@ void observe() { false, this, UserHandle.USER_ALL); resolver.registerContentObserver(LOCK_SCREEN_SHOW_NOTIFICATIONS, false, this, UserHandle.USER_ALL); + resolver.registerContentObserver(NOTIFICATION_SOUND_VIB_SCREEN_ON, + false, this, UserHandle.USER_ALL); update(null); } @@ -1958,6 +1968,11 @@ public void update(Uri uri) { if (uri == null || LOCK_SCREEN_SHOW_NOTIFICATIONS.equals(uri)) { mPreferencesHelper.updateLockScreenShowNotifications(); } + if (uri == null || NOTIFICATION_SOUND_VIB_SCREEN_ON.equals(uri)) { + mSoundVibScreenOn = Settings.System.getIntForUser(resolver, + Settings.System.NOTIFICATION_SOUND_VIB_SCREEN_ON, 1, + UserHandle.USER_CURRENT) == 1; + } } } @@ -1977,34 +1992,39 @@ private boolean containsFlag(int haystack, int needle) { return (haystack & needle) != 0; } - public boolean isInLockDownMode() { - return mIsInLockDownMode; + // Return whether the user is in lockdown mode. + // If the flag is not set, we assume the user is not in lockdown. + public boolean isInLockDownMode(int userId) { + return mUserInLockDownMode.get(userId, false); } @Override public synchronized void onStrongAuthRequiredChanged(int userId) { boolean userInLockDownModeNext = containsFlag(getStrongAuthForUser(userId), STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN); - mUserInLockDownMode.put(userId, userInLockDownModeNext); - boolean isInLockDownModeNext = mUserInLockDownMode.indexOfValue(true) != -1; - if (mIsInLockDownMode == isInLockDownModeNext) { + // Nothing happens if the lockdown mode of userId keeps the same. + if (userInLockDownModeNext == isInLockDownMode(userId)) { return; } - if (isInLockDownModeNext) { - cancelNotificationsWhenEnterLockDownMode(); + // When the lockdown mode is changed, we perform the following steps. + // If the userInLockDownModeNext is true, all the function calls to + // notifyPostedLocked and notifyRemovedLocked will not be executed. + // The cancelNotificationsWhenEnterLockDownMode calls notifyRemovedLocked + // and postNotificationsWhenExitLockDownMode calls notifyPostedLocked. + // So we shall call cancelNotificationsWhenEnterLockDownMode before + // we set mUserInLockDownMode as true. + // On the other hand, if the userInLockDownModeNext is false, we shall call + // postNotificationsWhenExitLockDownMode after we put false into mUserInLockDownMode + if (userInLockDownModeNext) { + cancelNotificationsWhenEnterLockDownMode(userId); } - // When the mIsInLockDownMode is true, both notifyPostedLocked and - // notifyRemovedLocked will be dismissed. So we shall call - // cancelNotificationsWhenEnterLockDownMode before we set mIsInLockDownMode - // as true and call postNotificationsWhenExitLockDownMode after we set - // mIsInLockDownMode as false. - mIsInLockDownMode = isInLockDownModeNext; + mUserInLockDownMode.put(userId, userInLockDownModeNext); - if (!isInLockDownModeNext) { - postNotificationsWhenExitLockDownMode(); + if (!userInLockDownModeNext) { + postNotificationsWhenExitLockDownMode(userId); } } } @@ -2754,6 +2774,7 @@ void onBootPhase(int phase, Looper mainLooper) { maybeShowInitialReviewPermissionsNotification(); } else if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) { mSnoozeHelper.scheduleRepostsForPersistedNotifications(System.currentTimeMillis()); + mAppLockManagerService = LocalServices.getService(AppLockManagerServiceInternal.class); } } @@ -3588,8 +3609,8 @@ public boolean areBubblesEnabled(UserHandle user) { public int getBubblePreferenceForPackage(String pkg, int uid) { enforceSystemOrSystemUIOrSamePackage(pkg, "Caller not system or systemui or same package"); - - if (UserHandle.getCallingUserId() != UserHandle.getUserId(uid)) { + final int userId = UserHandle.getUserId(uid); + if (UserHandle.getCallingUserId() != userId) { getContext().enforceCallingPermission( android.Manifest.permission.INTERACT_ACROSS_USERS, "getBubblePreferenceForPackage for uid " + uid); @@ -4368,7 +4389,8 @@ private StatusBarNotification sanitizeSbn(String pkg, int userId, sbn.getOpPkg(), sbn.getId(), sbn.getTag(), sbn.getUid(), sbn.getInitialPid(), notification, - sbn.getUser(), sbn.getOverrideGroupKey(), sbn.getPostTime()); + sbn.getUser(), sbn.getOverrideGroupKey(), + sbn.getPostTime(), sbn.getIsContentSecure()); } } return null; @@ -4953,10 +4975,10 @@ public String addAutomaticZenRule(AutomaticZenRule automaticZenRule, String pkg) } enforcePolicyAccess(Binder.getCallingUid(), "addAutomaticZenRule"); - // If the caller is system, take the package name from the rule's owner rather than - // from the caller's package. + // If the calling app is the system (from any user), take the package name from the + // rule's owner rather than from the caller's package. String rulePkg = pkg; - if (isCallingUidSystem()) { + if (isCallingAppIdSystem()) { if (automaticZenRule.getOwner() != null) { rulePkg = automaticZenRule.getOwner().getPackageName(); } @@ -5764,6 +5786,16 @@ public long pullStats(long startNs, int report, boolean doAgg, Slog.e(TAG, "exiting pullStats: bad request"); return 0; } + + @Override + public void forceShowLedLight(int color) { + forceShowLed(color); + } + + @Override + public void forcePulseLedLight(int color, int onTime, int offTime) { + forcePulseLed(color, onTime, offTime); + } }; protected void checkNotificationListenerAccess() { @@ -5946,6 +5978,8 @@ NotificationRecord createAutoGroupSummary(int userId, String pkg, String trigger 0, appIntent, PendingIntent.FLAG_IMMUTABLE, null, pkg, appInfo.uid); } + final boolean isContentSecure = mAppLockManagerService != null && + mAppLockManagerService.shouldRedactNotification(pkg, userId); final StatusBarNotification summarySbn = new StatusBarNotification(adjustedSbn.getPackageName(), adjustedSbn.getOpPkg(), @@ -5953,13 +5987,16 @@ NotificationRecord createAutoGroupSummary(int userId, String pkg, String trigger GroupHelper.AUTOGROUP_KEY, adjustedSbn.getUid(), adjustedSbn.getInitialPid(), summaryNotification, adjustedSbn.getUser(), GroupHelper.AUTOGROUP_KEY, - System.currentTimeMillis()); + System.currentTimeMillis(), isContentSecure); summaryRecord = new NotificationRecord(getContext(), summarySbn, notificationRecord.getChannel()); summaryRecord.setImportanceFixed(isPermissionFixed); summaryRecord.setIsAppImportanceLocked( notificationRecord.getIsAppImportanceLocked()); summaries.put(pkg, summarySbn.getKey()); + summaryRecord.setBubbleUpSuppressedByAppLock( + mAppLockManagerService != null && + mAppLockManagerService.requireUnlock(pkg, userId)); } if (summaryRecord != null && checkDisqualifyingFeatures(userId, uid, summaryRecord.getSbn().getId(), summaryRecord.getSbn().getTag(), summaryRecord, @@ -6386,6 +6423,33 @@ public void cleanupHistoryFiles() { checkCallerIsSystem(); mHistoryManager.cleanupHistoryFiles(); } + + @Override + public void updateSecureNotifications(String pkg, boolean isContentSecure, + boolean isBubbleUpSuppressed, int userId) { + mHandler.post(() -> updateSecureNotificationsInternal(pkg, isContentSecure, + isBubbleUpSuppressed, userId)); + } + + private void updateSecureNotificationsInternal(String pkg, boolean isContentSecure, + boolean isBubbleUpSuppressed, int userId) { + synchronized (mNotificationLock) { + for (int i = 0; i < mNotificationList.size(); i++) { + final NotificationRecord nr = mNotificationList.get(i); + final StatusBarNotification sbn = nr.getSbn(); + if (UserHandle.getUserId(sbn.getUid()) == userId + && sbn.getPackageName().equals(pkg)) { + if (sbn.getIsContentSecure() != isContentSecure || + nr.isBubbleUpSuppressedByAppLock() != isBubbleUpSuppressed) { + sbn.setIsContentSecure(isContentSecure); + nr.setBubbleUpSuppressedByAppLock(isBubbleUpSuppressed); + mListeners.notifyPostedLocked(nr, nr); + } + } + } + } + mRankingHandler.requestSort(); + } }; int getNumNotificationChannelsForPackage(String pkg, int uid, boolean includeDeleted) { @@ -6501,9 +6565,11 @@ void enqueueNotificationInternal(final String pkg, final String opPkg, final int mUsageStats.registerEnqueuedByApp(pkg); + final boolean isContentSecure = mAppLockManagerService != null && + mAppLockManagerService.shouldRedactNotification(pkg, userId); final StatusBarNotification n = new StatusBarNotification( pkg, opPkg, id, tag, notificationUid, callingPid, notification, - user, null, System.currentTimeMillis()); + user, null, System.currentTimeMillis(), isContentSecure); // setup local book-keeping String channelId = notification.getChannelId(); @@ -6544,6 +6610,8 @@ void enqueueNotificationInternal(final String pkg, final String opPkg, final int r.setPostSilently(postSilently); r.setFlagBubbleRemoved(false); r.setPkgAllowedAsConvo(mMsgPkgsAllowedAsConvos.contains(pkg)); + r.setBubbleUpSuppressedByAppLock(mAppLockManagerService != null && + mAppLockManagerService.requireUnlock(pkg, userId)); boolean isImportanceFixed = mPermissionHelper.isPermissionFixed(pkg, userId); r.setImportanceFixed(isImportanceFixed); @@ -7870,7 +7938,8 @@ && isNotificationForCurrentUser(record)) { } if (aboveThreshold && isNotificationForCurrentUser(record)) { - if (mSystemReady && mAudioManager != null) { + boolean skipSound = mScreenOn && !mSoundVibScreenOn; + if (mSystemReady && mAudioManager != null && !skipSound) { Uri soundUri = record.getSound(); hasValidSound = soundUri != null && !Uri.EMPTY.equals(soundUri); VibrationEffect vibration = record.getVibration(); @@ -8023,6 +8092,22 @@ boolean canShowLightsLocked(final NotificationRecord record, boolean aboveThresh return true; } + private void forceShowLed(int color) { + if (color != -1) { + mNotificationLight.setColor(color); + } else { + mNotificationLight.turnOff(); + } + } + + private void forcePulseLed(int color, int onTime, int offTime) { + if (color != -1) { + mNotificationLight.setFlashing(color, LogicalLight.LIGHT_FLASH_TIMED, onTime, offTime); + } else { + mNotificationLight.turnOff(); + } + } + @GuardedBy("mNotificationLock") boolean isInsistentUpdate(final NotificationRecord record) { return (Objects.equals(record.getKey(), mSoundNotificationKey) @@ -8535,13 +8620,15 @@ static class NotificationRecordExtractorData { float mRankingScore; boolean mIsConversation; + boolean mIsBubbleUpSuppressedByAppLock; + NotificationRecordExtractorData(int position, int visibility, boolean showBadge, boolean allowBubble, boolean isBubble, NotificationChannel channel, String groupKey, ArrayList overridePeople, ArrayList snoozeCriteria, Integer userSentiment, Integer suppressVisually, ArrayList systemSmartActions, ArrayList smartReplies, int importance, float rankingScore, - boolean isConversation) { + boolean isConversation, boolean isBubbleUpSuppressedByAppLock) { mPosition = position; mVisibility = visibility; mShowBadge = showBadge; @@ -8558,6 +8645,7 @@ static class NotificationRecordExtractorData { mImportance = importance; mRankingScore = rankingScore; mIsConversation = isConversation; + mIsBubbleUpSuppressedByAppLock = isBubbleUpSuppressedByAppLock; } // Returns whether the provided NotificationRecord differs from the cached data in any way. @@ -8576,7 +8664,8 @@ boolean hasDiffForRankingLocked(NotificationRecord r, int newPosition) { || !Objects.equals(mSuppressVisually, r.getSuppressedVisualEffects()) || !Objects.equals(mSystemSmartActions, r.getSystemGeneratedSmartActions()) || !Objects.equals(mSmartReplies, r.getSmartReplies()) - || mImportance != r.getImportance(); + || mImportance != r.getImportance() + || mIsBubbleUpSuppressedByAppLock != r.isBubbleUpSuppressedByAppLock(); } // Returns whether the NotificationRecord has a change from this data for which we should @@ -8627,7 +8716,8 @@ void handleRankingSort() { r.getSmartReplies(), r.getImportance(), r.getRankingScore(), - r.isConversation()); + r.isConversation(), + r.isBubbleUpSuppressedByAppLock()); extractorDataBefore.put(r.getKey(), extractorData); mRankingHelper.extractSignals(r); } @@ -9671,11 +9761,14 @@ private void unhideNotificationsForPackages(@NonNull String[] pkgs, } } - private void cancelNotificationsWhenEnterLockDownMode() { + private void cancelNotificationsWhenEnterLockDownMode(int userId) { synchronized (mNotificationLock) { int numNotifications = mNotificationList.size(); for (int i = 0; i < numNotifications; i++) { NotificationRecord rec = mNotificationList.get(i); + if (rec.getUser().getIdentifier() != userId) { + continue; + } mListeners.notifyRemovedLocked(rec, REASON_CANCEL_ALL, rec.getStats()); } @@ -9683,14 +9776,23 @@ private void cancelNotificationsWhenEnterLockDownMode() { } } - private void postNotificationsWhenExitLockDownMode() { + private void postNotificationsWhenExitLockDownMode(int userId) { synchronized (mNotificationLock) { int numNotifications = mNotificationList.size(); + // Set the delay to spread out the burst of notifications. + long delay = 0; for (int i = 0; i < numNotifications; i++) { NotificationRecord rec = mNotificationList.get(i); - mListeners.notifyPostedLocked(rec, rec); + if (rec.getUser().getIdentifier() != userId) { + continue; + } + mHandler.postDelayed(() -> { + synchronized (mNotificationLock) { + mListeners.notifyPostedLocked(rec, rec); + } + }, delay); + delay += 20; } - } } @@ -9705,6 +9807,12 @@ protected boolean isCallingUidSystem() { return uid == Process.SYSTEM_UID; } + protected boolean isCallingAppIdSystem() { + final int uid = Binder.getCallingUid(); + final int appid = UserHandle.getAppId(uid); + return appid == Process.SYSTEM_UID; + } + protected boolean isUidSystemOrPhone(int uid) { final int appid = UserHandle.getAppId(uid); return (appid == Process.SYSTEM_UID || appid == Process.PHONE_UID @@ -9869,6 +9977,9 @@ NotificationRankingUpdate makeRankingUpdateLocked(ManagedServiceInfo info) { for (int i = 0; i < N; i++) { NotificationRecord record = mNotificationList.get(i); + if (isInLockDownMode(record.getUser().getIdentifier())) { + continue; + } if (!isVisibleToListener(record.getSbn(), record.getNotificationType(), info)) { continue; } @@ -9910,8 +10021,8 @@ NotificationRankingUpdate makeRankingUpdateLocked(ManagedServiceInfo info) { rankings.toArray(new NotificationListenerService.Ranking[0])); } - boolean isInLockDownMode() { - return mStrongAuthTracker.isInLockDownMode(); + boolean isInLockDownMode(int userId) { + return mStrongAuthTracker.isInLockDownMode(userId); } boolean hasCompanionDevice(ManagedServiceInfo info) { @@ -10967,7 +11078,7 @@ public void notifyPostedLocked(NotificationRecord r, NotificationRecord old) { @GuardedBy("mNotificationLock") void notifyPostedLocked(NotificationRecord r, NotificationRecord old, boolean notifyAllListeners) { - if (isInLockDownMode()) { + if (isInLockDownMode(r.getUser().getIdentifier())) { return; } @@ -11073,7 +11184,7 @@ private void updateUriPermissionsForActiveNotificationsLocked( @GuardedBy("mNotificationLock") public void notifyRemovedLocked(NotificationRecord r, int reason, NotificationStats notificationStats) { - if (isInLockDownMode()) { + if (isInLockDownMode(r.getUser().getIdentifier())) { return; } @@ -11122,10 +11233,6 @@ public void notifyRemovedLocked(NotificationRecord r, int reason, */ @GuardedBy("mNotificationLock") public void notifyRankingUpdateLocked(List changedHiddenNotifications) { - if (isInLockDownMode()) { - return; - } - boolean isHiddenRankingUpdate = changedHiddenNotifications != null && changedHiddenNotifications.size() > 0; // TODO (b/73052211): if the ranking update changed the notification type, diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java index cbaf485c077f..6b9db419c0fe 100644 --- a/services/core/java/com/android/server/notification/NotificationRecord.java +++ b/services/core/java/com/android/server/notification/NotificationRecord.java @@ -211,6 +211,8 @@ public final class NotificationRecord { // are sorted. private boolean mPendingLogUpdate = false; + private boolean mIsBubbleUpSuppressedByAppLock = false; + public NotificationRecord(Context context, StatusBarNotification sbn, NotificationChannel channel) { this.sbn = sbn; @@ -1582,6 +1584,14 @@ public ArraySet getPhoneNumbers() { return mPhoneNumbers; } + public void setBubbleUpSuppressedByAppLock(boolean suppressed) { + mIsBubbleUpSuppressedByAppLock = suppressed; + } + + public boolean isBubbleUpSuppressedByAppLock() { + return mIsBubbleUpSuppressedByAppLock; + } + @VisibleForTesting static final class Light { public final int color; diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java index d8aa469bcd81..78dad124a4c1 100644 --- a/services/core/java/com/android/server/notification/PreferencesHelper.java +++ b/services/core/java/com/android/server/notification/PreferencesHelper.java @@ -852,7 +852,9 @@ public void createNotificationChannelGroup(String pkg, int uid, NotificationChan Objects.requireNonNull(pkg); Objects.requireNonNull(group); Objects.requireNonNull(group.getId()); - Objects.requireNonNull(!TextUtils.isEmpty(group.getName())); + if (TextUtils.isEmpty(group.getName())) { + throw new IllegalArgumentException("group.getName() can't be empty"); + } boolean needsDndChange = false; synchronized (mPackagePreferences) { PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid); diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java index 46b7460dff1b..51243cfba854 100644 --- a/services/core/java/com/android/server/pm/ComputerEngine.java +++ b/services/core/java/com/android/server/pm/ComputerEngine.java @@ -152,6 +152,8 @@ import com.android.server.utils.WatchedSparseIntArray; import com.android.server.wm.ActivityTaskManagerInternal; +import ink.kaleidoscope.server.GmsManagerService; + import libcore.util.EmptyArray; import java.io.BufferedOutputStream; @@ -948,6 +950,8 @@ public final ApplicationInfo generateApplicationInfoFromSettings(String packageN public final ApplicationInfo getApplicationInfo(String packageName, @PackageManager.ApplicationInfoFlagsBits long flags, int userId) { + if (GmsManagerService.shouldHide(userId, packageName)) + return null; return getApplicationInfoInternal(packageName, flags, Binder.getCallingUid(), userId); } @@ -961,6 +965,8 @@ public final ApplicationInfo getApplicationInfoInternal(String packageName, @PackageManager.ApplicationInfoFlagsBits long flags, int filterCallingUid, int userId) { if (!mUserManager.exists(userId)) return null; + if (GmsManagerService.shouldHide(userId, packageName)) + return null; flags = updateFlagsForApplication(flags, userId); if (!isRecentsAccessingChildProfiles(Binder.getCallingUid(), userId)) { @@ -1603,6 +1609,29 @@ private List maybeAddInstantAppInstaller(List result, return result; } + private boolean requestsFakeSignature(AndroidPackage p) { + return p.getMetaData() != null && + p.getMetaData().getString("fake-signature") != null; + } + + private PackageInfo mayFakeSignature(AndroidPackage p, PackageInfo pi, + Set permissions) { + try { + if (p.getMetaData() != null && + p.getTargetSdkVersion() > Build.VERSION_CODES.LOLLIPOP_MR1) { + String sig = p.getMetaData().getString("fake-signature"); + if (sig != null && + permissions.contains("android.permission.FAKE_PACKAGE_SIGNATURE")) { + pi.signatures = new Signature[] {new Signature(sig)}; + } + } + } catch (Throwable t) { + // We should never die because of any failures, this is system code! + Log.w("PackageManagerService.FAKE_PACKAGE_SIGNATURE", t); + } + return pi; + } + public final PackageInfo generatePackageInfo(PackageStateInternal ps, @PackageManager.PackageInfoFlagsBits long flags, int userId) { if (!mUserManager.exists(userId)) return null; @@ -1632,13 +1661,15 @@ public final PackageInfo generatePackageInfo(PackageStateInternal ps, final int[] gids = (flags & PackageManager.GET_GIDS) == 0 ? EMPTY_INT_ARRAY : mPermissionManager.getGidsForUid(UserHandle.getUid(userId, ps.getAppId())); // Compute granted permissions only if package has requested permissions - final Set permissions = ((flags & PackageManager.GET_PERMISSIONS) == 0 + final Set permissions = (((flags & PackageManager.GET_PERMISSIONS) == 0 + && !requestsFakeSignature(p)) || ArrayUtils.isEmpty(p.getRequestedPermissions())) ? Collections.emptySet() : mPermissionManager.getGrantedPermissions(ps.getPackageName(), userId); - PackageInfo packageInfo = PackageInfoUtils.generate(p, gids, flags, + PackageInfo packageInfo = mayFakeSignature(p, PackageInfoUtils.generate(p, gids, flags, state.getFirstInstallTime(), ps.getLastUpdateTime(), permissions, state, userId, - ps); + ps), + permissions); if (packageInfo == null) { return null; @@ -1681,6 +1712,8 @@ public final PackageInfo generatePackageInfo(PackageStateInternal ps, public final PackageInfo getPackageInfo(String packageName, @PackageManager.PackageInfoFlagsBits long flags, int userId) { + if (GmsManagerService.shouldHide(userId, packageName)) + return null; return getPackageInfoInternal(packageName, PackageManager.VERSION_CODE_HIGHEST, flags, Binder.getCallingUid(), userId); } @@ -1694,6 +1727,8 @@ public final PackageInfo getPackageInfo(String packageName, public final PackageInfo getPackageInfoInternal(String packageName, long versionCode, long flags, int filterCallingUid, int userId) { if (!mUserManager.exists(userId)) return null; + if (GmsManagerService.shouldHide(userId, packageName)) + return null; flags = updateFlagsForPackage(flags, userId); enforceCrossUserPermission(Binder.getCallingUid(), userId, false /* requireFullPermission */, false /* checkShell */, "get package info"); @@ -1790,7 +1825,8 @@ public final ParceledListSlice getInstalledPackages(long flags, int enforceCrossUserPermission(callingUid, userId, false /* requireFullPermission */, false /* checkShell */, "get installed packages"); - return getInstalledPackagesBody(flags, userId, callingUid); + return GmsManagerService.recreatePackageList( + userId, getInstalledPackagesBody(flags, userId, callingUid)); } protected ParceledListSlice getInstalledPackagesBody(long flags, int userId, @@ -4748,7 +4784,7 @@ public List getInstalledApplications( } } - return list; + return GmsManagerService.recreateApplicationList(userId, list); } @Nullable diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java index 5e0fc3bf91e7..c568e3c622b6 100644 --- a/services/core/java/com/android/server/pm/LauncherAppsService.java +++ b/services/core/java/com/android/server/pm/LauncherAppsService.java @@ -99,6 +99,8 @@ import com.android.server.pm.parsing.pkg.AndroidPackage; import com.android.server.wm.ActivityTaskManagerInternal; +import ink.kaleidoscope.server.ParallelSpaceManagerService; + import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -1355,7 +1357,8 @@ public void showAppDetailsAsUser(IApplicationThread caller, private boolean isEnabledProfileOf(UserHandle listeningUser, UserHandle user, String debugMsg) { return mUserManagerInternal.isProfileAccessible(listeningUser.getIdentifier(), - user.getIdentifier(), debugMsg, false); + user.getIdentifier(), debugMsg, false) || + ParallelSpaceManagerService.isCurrentParallelUser(user.getIdentifier()); } /** Returns whether or not the result to the listener should be filtered. */ diff --git a/services/core/java/com/android/server/pm/PackageHandler.java b/services/core/java/com/android/server/pm/PackageHandler.java index 0dfa31c5f1fc..e31e21c8b96d 100644 --- a/services/core/java/com/android/server/pm/PackageHandler.java +++ b/services/core/java/com/android/server/pm/PackageHandler.java @@ -281,6 +281,11 @@ void doHandleMessage(Message msg) { + " not found. It may be invalid or overridden by verifier"); break; } + if (state.isIntegrityVerificationComplete()) { + Slog.w(TAG, "Integrity verification with id " + verificationId + + " already complete."); + break; + } final int response = (Integer) msg.obj; final VerificationParams params = state.getVerificationParams(); diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index b7b332621e7f..4d4de6f1c7d3 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -241,6 +241,8 @@ import dalvik.system.VMRuntime; +import ink.kaleidoscope.server.ParallelSpaceManagerService; + import libcore.util.HexEncoding; import java.io.ByteArrayInputStream; @@ -858,6 +860,9 @@ public void enablePackageCaches() { private final PackageProperty mPackageProperty = new PackageProperty(); + ArrayList mDisabledComponentsList; + ArrayList mForceEnabledComponentsList; + final PendingPackageBroadcasts mPendingBroadcasts; static final int SEND_PENDING_BROADCAST = 1; @@ -1491,8 +1496,8 @@ context, lock, installer, installLock, new PackageAbiHelperImpl(), } PackageManagerService m = new PackageManagerService(injector, onlyCore, factoryTest, - PackagePartitions.FINGERPRINT, Build.IS_ENG, Build.IS_USERDEBUG, - Build.VERSION.SDK_INT, Build.VERSION.INCREMENTAL); + String.valueOf(Build.TIME), Build.IS_ENG, Build.IS_USERDEBUG, Build.VERSION.SDK_INT, + Build.FINGERPRINT); t.traceEnd(); // "create package manager" final CompatChange.ChangeListener selinuxChangeListener = packageName -> { @@ -1549,6 +1554,38 @@ private void installAllowlistedSystemPackages() { } } + private void loadForceEnabledComponents(){ + String[] components = mContext.getResources().getStringArray( + com.android.internal.R.array.config_forceEnabledComponents); + for (String name : components) { + ComponentName cn = ComponentName.unflattenFromString(name); + mForceEnabledComponentsList.add(cn); + } + } + + private void enableComponents(String[] components, boolean enable) { + // Disable or enable components marked at build-time + for (String name : components) { + ComponentName cn = ComponentName.unflattenFromString(name); + if (!enable) { + mDisabledComponentsList.add(cn); + } + Slog.v(TAG, "Changing enabled state of " + name + " to " + enable); + String className = cn.getClassName(); + PackageSetting pkgSetting = mSettings.mPackages.get(cn.getPackageName()); + if (pkgSetting == null || pkgSetting.getPkg() == null + || !AndroidPackageUtils.hasComponentClassName(pkgSetting.getPkg(), className)) { + Slog.w(TAG, "Unable to change enabled state of " + name + " to " + enable); + continue; + } + if (enable) { + pkgSetting.enableComponentLPw(className, UserHandle.USER_OWNER); + } else { + pkgSetting.disableComponentLPw(className, UserHandle.USER_OWNER); + } + } + } + // Link watchables to the class @SuppressWarnings("GuardedBy") private void registerObservers(boolean verify) { @@ -1952,8 +1989,8 @@ public boolean hasFeature(String feature) { mIsUpgrade = !buildFingerprint.equals(ver.fingerprint); if (mIsUpgrade) { - PackageManagerServiceUtils.logCriticalInfo(Log.INFO, "Upgrading from " - + ver.fingerprint + " to " + PackagePartitions.FINGERPRINT); + PackageManagerServiceUtils.logCriticalInfo(Log.INFO, + "Upgrading from " + ver.fingerprint + " to " + Build.TIME); } mInitAppsHelper = new InitAppsHelper(this, mApexManager, mInstallPackageHelper, @@ -1983,7 +2020,7 @@ public boolean hasFeature(String feature) { } mCacheDir = PackageManagerServiceUtils.preparePackageParserCache( - mIsEngBuild, mIsUserDebugBuild, mIncrementalVersion); + mIsEngBuild, mIsUserDebugBuild, mIncrementalVersion, mIsUpgrade); final int[] userIds = mUserManager.getUserIds(); PackageParser2 packageParser = mInjector.getScanningCachingPackageParser(); @@ -2068,8 +2105,7 @@ public boolean hasFeature(String feature) { // this situation. if (mIsUpgrade) { Slog.i(TAG, "Build fingerprint changed from " + ver.fingerprint + " to " - + PackagePartitions.FINGERPRINT - + "; regranting permissions for internal storage"); + + Build.TIME + "; regranting permissions for internal storage"); } mPermissionManager.onStorageVolumeMounted( StorageManager.UUID_PRIVATE_INTERNAL, mIsUpgrade); @@ -2084,6 +2120,19 @@ public boolean hasFeature(String feature) { } } + // Disable components marked for disabling at build-time + mDisabledComponentsList = new ArrayList(); + enableComponents(mContext.getResources().getStringArray( + com.android.internal.R.array.config_deviceDisabledComponents), false); + enableComponents(mContext.getResources().getStringArray( + com.android.internal.R.array.config_globallyDisabledComponents), false); + + // Enable components marked for forced-enable at build-time + mForceEnabledComponentsList = new ArrayList(); + enableComponents(mContext.getResources().getStringArray( + com.android.internal.R.array.config_forceEnabledComponents), true); + loadForceEnabledComponents(); + // If this is first boot after an OTA, and a normal boot, then // we need to clear code cache directories. // Note that we do *not* clear the application profiles. These remain valid @@ -2101,7 +2150,7 @@ public boolean hasFeature(String feature) { | Installer.FLAG_CLEAR_APP_DATA_KEEP_ART_PROFILES); } } - ver.fingerprint = PackagePartitions.FINGERPRINT; + ver.fingerprint = String.valueOf(Build.TIME); } // Defer the app data fixup until we are done with app data clearing above. @@ -3188,6 +3237,22 @@ public void deletePackageVersioned(VersionedPackage versionedPackage, final IPackageDeleteObserver2 observer, final int userId, final int deleteFlags) { mDeletePackageHelper.deletePackageVersionedInternal( versionedPackage, observer, userId, deleteFlags, false); + + // Delete for parallel users if the package is deleted in their owner. + if (!ParallelSpaceManagerService.isCurrentParallelOwner(userId)) + return; + final long token = Binder.clearCallingIdentity(); + try { + for (int parallelUserId : ParallelSpaceManagerService.getCurrentParallelUserIds()) { + mDeletePackageHelper.deletePackageVersionedInternal(versionedPackage, + new PackageInstallerService.PackageDeleteObserverAdapter( + mContext, null, versionedPackage.getPackageName(), + false, parallelUserId) + .getBinder(), parallelUserId, 0, true); + } + } finally { + Binder.restoreCallingIdentity(token); + } } boolean isCallerVerifier(@NonNull Computer snapshot, int callingUid) { @@ -5568,6 +5633,20 @@ public void setComponentEnabledSetting(ComponentName componentName, int newState, int flags, int userId) { if (!mUserManager.exists(userId)) return; + // Don't allow to enable components marked for disabling at build-time + if (mDisabledComponentsList.contains(componentName)) { + Slog.d(TAG, "Ignoring attempt to set enabled state of disabled component " + + componentName.flattenToString()); + return; + } + + // Don't allow to control components forced enabled at build-time + if (mForceEnabledComponentsList.contains(componentName)) { + Slog.d(TAG, "Ignoring attempt to control forced enabled component " + + componentName.flattenToString()); + return; + } + setEnabledSettings(List.of(new PackageManager.ComponentEnabledSetting(componentName, newState, flags)), userId, null /* callingPackage */); } diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java index 3443d455ee82..b05775e28582 100644 --- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java +++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java @@ -1294,7 +1294,7 @@ public static void enforceSystemOrRoot(String message) { } public static @Nullable File preparePackageParserCache(boolean forEngBuild, - boolean isUserDebugBuild, String incrementalVersion) { + boolean isUserDebugBuild, String incrementalVersion, boolean isUpgrade) { if (!FORCE_PACKAGE_PARSED_CACHE_ENABLED) { if (!DEFAULT_PACKAGE_PARSER_CACHE_ENABLED) { return null; @@ -1321,11 +1321,11 @@ public static void enforceSystemOrRoot(String message) { // identify cached items. In particular, changing the value of certain // feature flags should cause us to invalidate any caches. final String cacheName = FORCE_PACKAGE_PARSED_CACHE_ENABLED ? "debug" - : PackagePartitions.FINGERPRINT; + : SystemProperties.digestOf(String.valueOf(Build.TIME)); // Reconcile cache directories, keeping only what we'd actually use. for (File cacheDir : FileUtils.listFilesOrEmpty(cacheBaseDir)) { - if (Objects.equals(cacheName, cacheDir.getName())) { + if (!isUpgrade && Objects.equals(cacheName, cacheDir.getName())) { Slog.d(TAG, "Keeping known cache " + cacheDir.getName()); } else { Slog.d(TAG, "Destroying unknown cache " + cacheDir.getName()); @@ -1350,9 +1350,7 @@ public static void enforceSystemOrRoot(String message) { // NOTE: When no BUILD_NUMBER is set by the build system, it defaults to a build // that starts with "eng." to signify that this is an engineering build and not // destined for release. - if (isUserDebugBuild && incrementalVersion.startsWith("eng.")) { - Slog.w(TAG, "Wiping cache directory because the system partition changed."); - + /*if (isUserDebugBuild && incrementalVersion.startsWith("eng.")) { // Heuristic: If the /system directory has been modified recently due to an "adb sync" // or a regular make, then blow away the cache. Note that mtimes are *NOT* reliable // in general and should not be used for production changes. In this specific case, @@ -1360,10 +1358,11 @@ public static void enforceSystemOrRoot(String message) { File frameworkDir = new File(Environment.getRootDirectory(), "framework"); if (cacheDir.lastModified() < frameworkDir.lastModified()) { + Slog.w(TAG, "Wiping cache directory because the system partition changed."); FileUtils.deleteContents(cacheBaseDir); cacheDir = FileUtils.createDir(cacheBaseDir, cacheName); } - } + }*/ return cacheDir; } diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java index 1eb74facb840..4fecba10152f 100644 --- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java +++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java @@ -3031,7 +3031,9 @@ public int runGetMaxRunningUsers() { private static class InstallParams { SessionParams sessionParams; String installerPackageName; - int userId = UserHandle.USER_ALL; + // Make sure it won't be installed into parallel users + // via `adb install` command by default. + int userId = UserHandle.USER_CURRENT; long stagedReadyTimeoutMs = DEFAULT_STAGED_READY_TIMEOUT_MS; } diff --git a/services/core/java/com/android/server/pm/PerPackageReadTimeouts.java b/services/core/java/com/android/server/pm/PerPackageReadTimeouts.java index 3b306a850b64..b310c62aafb4 100644 --- a/services/core/java/com/android/server/pm/PerPackageReadTimeouts.java +++ b/services/core/java/com/android/server/pm/PerPackageReadTimeouts.java @@ -16,7 +16,7 @@ package com.android.server.pm; -import android.annotation.NonNull;; +import android.annotation.NonNull; import android.text.TextUtils; import com.android.internal.util.HexDump; diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 7437b145189f..d19d4ff89178 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -44,7 +44,6 @@ import android.content.pm.PackageInstaller; import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; -import android.content.pm.PackagePartitions; import android.content.pm.PermissionInfo; import android.content.pm.ResolveInfo; import android.content.pm.Signature; @@ -433,7 +432,7 @@ public static class VersionInfo { int databaseVersion; /** - * Last known value of {@link Build#FINGERPRINT}. Used to determine when + * Last known value of {@link Build#TIME}. Used to determine when * an system update has occurred, meaning we need to clear code caches. */ String fingerprint; @@ -445,7 +444,7 @@ public static class VersionInfo { public void forceCurrent() { sdkVersion = Build.VERSION.SDK_INT; databaseVersion = CURRENT_DATABASE_VERSION; - fingerprint = PackagePartitions.FINGERPRINT; + fingerprint = String.valueOf(Build.TIME); } } @@ -1052,7 +1051,11 @@ void pruneSharedUsersLPw() { pkgSetting.setAppId(sharedUser.mAppId); } else { // Clone the setting here for disabled system packages - if (disabledPkg != null) { + if (disabledPkg != null + && !(disabledPkg.getPath() == null + || !disabledPkg.getPath().exists() + || disabledPkg.getPkg() == null) + && !disabledPkg.hasSharedUser()) { // For disabled packages a new setting is created // from the existing user id. This still has to be // added to list of user id's @@ -5477,7 +5480,7 @@ public void setPermissionControllerVersion(long version) { } private String getExtendedFingerprint(long version) { - return PackagePartitions.FINGERPRINT + "?pc_version=" + version; + return Build.TIME + "?pc_version=" + version; } private static long uniformRandom(double low, double high) { diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java index 0c601bfde05a..890c89152a7c 100644 --- a/services/core/java/com/android/server/pm/ShortcutPackage.java +++ b/services/core/java/com/android/server/pm/ShortcutPackage.java @@ -1962,10 +1962,15 @@ public static ShortcutPackage loadFromXml(ShortcutService s, ShortcutUser shortc continue; case TAG_SHORTCUT: - final ShortcutInfo si = parseShortcut(parser, packageName, - shortcutUser.getUserId(), fromBackup); - // Don't use addShortcut(), we don't need to save the icon. - ret.mShortcuts.put(si.getId(), si); + try { + final ShortcutInfo si = parseShortcut(parser, packageName, + shortcutUser.getUserId(), fromBackup); + // Don't use addShortcut(), we don't need to save the icon. + ret.mShortcuts.put(si.getId(), si); + } catch (Exception e) { + // b/246540168 malformed shortcuts should be ignored + Slog.e(TAG, "Failed parsing shortcut.", e); + } continue; case TAG_SHARE_TARGET: ret.mShareTargets.add(ShareTargetInfo.loadFromXml(parser)); diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java index 0b20683185f0..051bd7214699 100644 --- a/services/core/java/com/android/server/pm/ShortcutService.java +++ b/services/core/java/com/android/server/pm/ShortcutService.java @@ -5139,7 +5139,7 @@ void injectRestoreCallingIdentity(long token) { // Injection point. String injectBuildFingerprint() { - return Build.FINGERPRINT; + return String.valueOf(Build.TIME); } final void wtf(String message) { diff --git a/services/core/java/com/android/server/pm/StorageEventHelper.java b/services/core/java/com/android/server/pm/StorageEventHelper.java index 666776b4161e..b43bc04aebce 100644 --- a/services/core/java/com/android/server/pm/StorageEventHelper.java +++ b/services/core/java/com/android/server/pm/StorageEventHelper.java @@ -33,6 +33,7 @@ import android.content.pm.PackagePartitions; import android.content.pm.UserInfo; import android.content.pm.VersionedPackage; +import android.os.Build; import android.os.Environment; import android.os.FileUtils; import android.os.UserHandle; @@ -166,7 +167,7 @@ private void loadPrivatePackagesInner(VolumeInfo vol) { Slog.w(TAG, "Failed to scan " + ps.getPath() + ": " + e.getMessage()); } - if (!PackagePartitions.FINGERPRINT.equals(ver.fingerprint)) { + if (!String.valueOf(Build.TIME).equals(ver.fingerprint)) { appDataHelper.clearAppDataLIF( ps.getPkg(), UserHandle.USER_ALL, FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL | Installer.FLAG_CLEAR_CODE_CACHE_ONLY @@ -204,10 +205,10 @@ private void loadPrivatePackagesInner(VolumeInfo vol) { } synchronized (mPm.mLock) { - final boolean isUpgrade = !PackagePartitions.FINGERPRINT.equals(ver.fingerprint); + final boolean isUpgrade = !String.valueOf(Build.TIME).equals(ver.fingerprint); if (isUpgrade) { logCriticalInfo(Log.INFO, "Build fingerprint changed from " + ver.fingerprint - + " to " + PackagePartitions.FINGERPRINT + "; regranting permissions for " + + " to " + String.valueOf(Build.TIME) + "; regranting permissions for " + volumeUuid); } mPm.mPermissionManager.onStorageVolumeMounted(volumeUuid, isUpgrade); diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index 00fb0651adc4..79e79c0841e1 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -48,7 +48,6 @@ import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.PackageManagerInternal; -import android.content.pm.PackagePartitions; import android.content.pm.ShortcutServiceInternal; import android.content.pm.UserInfo; import android.content.pm.UserInfo.UserInfoFlag; @@ -95,6 +94,7 @@ import android.util.ArrayMap; import android.util.ArraySet; import android.util.AtomicFile; +import android.util.EventLog; import android.util.IndentingPrintWriter; import android.util.IntArray; import android.util.Slog; @@ -135,6 +135,8 @@ import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; +import ink.kaleidoscope.server.ParallelSpaceManagerService; + import java.io.File; import java.io.FileDescriptor; import java.io.FileInputStream; @@ -194,6 +196,7 @@ public class UserManagerService extends IUserManager.Stub { private static final String ATTR_PROFILE_GROUP_ID = "profileGroupId"; private static final String ATTR_PROFILE_BADGE = "profileBadge"; private static final String ATTR_RESTRICTED_PROFILE_PARENT_ID = "restrictedProfileParentId"; + private static final String ATTR_PARALLEL_PARENT_ID = "parallelParentId"; private static final String ATTR_SEED_ACCOUNT_NAME = "seedAccountName"; private static final String ATTR_SEED_ACCOUNT_TYPE = "seedAccountType"; private static final String TAG_GUEST_RESTRICTIONS = "guestRestrictions"; @@ -1591,7 +1594,8 @@ public boolean isMediaSharedWithParent(@UserIdInt int userId) { "isMediaSharedWithParent"); synchronized (mUsersLock) { UserTypeDetails userTypeDetails = getUserTypeDetailsNoChecks(userId); - return userTypeDetails != null ? userTypeDetails.isProfile() + return userTypeDetails != null ? + (userTypeDetails.isProfile() || userTypeDetails.isParallel()) && userTypeDetails.isMediaSharedWithParent() : false; } } @@ -1702,6 +1706,8 @@ private void checkManageOrInteractPermissionIfCallerInOtherProfileGroup(@UserIdI if (hasManageUsersPermission()) { return; } + if (ParallelSpaceManagerService.canInteract(callingUserId, userId)) + return; if (hasPermissionGranted(Manifest.permission.INTERACT_ACROSS_USERS, Binder.getCallingUid())) { return; @@ -2692,7 +2698,8 @@ private int getAliveUsersExcludingGuestsCountLU() { // Skip over users being removed for (int i = 0; i < totalUserCount; i++) { UserInfo user = mUsers.valueAt(i).info; - if (!mRemovingUserIds.get(user.id) && !user.isGuest() && !user.preCreated) { + if (!mRemovingUserIds.get(user.id) && !user.isGuest() && !user.preCreated && + !user.isParallel()) { aliveUserCount++; } } @@ -3452,6 +3459,9 @@ void writeUserLP(UserData userData, OutputStream os) serializer.attributeInt(null, ATTR_RESTRICTED_PROFILE_PARENT_ID, userInfo.restrictedProfileParentId); } + if (userInfo.parallelParentId != UserHandle.USER_NULL) { + serializer.attributeInt(null, ATTR_PARALLEL_PARENT_ID, userInfo.parallelParentId); + } // Write seed data if (userData.persistSeedData) { if (userData.seedAccountName != null) { @@ -3608,6 +3618,7 @@ UserData readUserLP(int id, InputStream is) throws IOException, RestrictionsSet localRestrictions = null; Bundle globalRestrictions = null; boolean ignorePrepareStorageErrors = true; // default is true for old users + int parallelParentId = UserHandle.USER_NULL; final TypedXmlPullParser parser = Xml.resolvePullParser(is); int type; @@ -3645,6 +3656,8 @@ UserData readUserLP(int id, InputStream is) throws IOException, preCreated = parser.getAttributeBoolean(null, ATTR_PRE_CREATED, false); converted = parser.getAttributeBoolean(null, ATTR_CONVERTED_FROM_PRE_CREATED, false); guestToRemove = parser.getAttributeBoolean(null, ATTR_GUEST_TO_REMOVE, false); + parallelParentId = parser.getAttributeInt(null, ATTR_PARALLEL_PARENT_ID, + UserHandle.USER_NULL); seedAccountName = parser.getAttributeValue(null, ATTR_SEED_ACCOUNT_NAME); seedAccountType = parser.getAttributeValue(null, ATTR_SEED_ACCOUNT_TYPE); @@ -3708,6 +3721,7 @@ UserData readUserLP(int id, InputStream is) throws IOException, userInfo.profileGroupId = profileGroupId; userInfo.profileBadge = profileBadge; userInfo.restrictedProfileParentId = restrictedProfileParentId; + userInfo.parallelParentId = parallelParentId; // Create the UserData object that's internal to this class UserData userData = new UserData(); @@ -3958,6 +3972,7 @@ private UserInfo createUserInternalUncheckedNoTracing(@Nullable String name, final boolean isRestricted = UserManager.isUserTypeRestricted(userType); final boolean isDemo = UserManager.isUserTypeDemo(userType); final boolean isManagedProfile = UserManager.isUserTypeManagedProfile(userType); + final boolean isParallel = userTypeDetails.isParallel(); final long ident = Binder.clearCallingIdentity(); UserInfo userInfo; @@ -3982,7 +3997,7 @@ private UserInfo createUserInternalUncheckedNoTracing(@Nullable String name, UserManager.USER_OPERATION_ERROR_MAX_USERS); } // Keep logic in sync with getRemainingCreatableUserCount() - if (!isGuest && !isManagedProfile && !isDemo && isUserLimitReached()) { + if (!isGuest && !isManagedProfile && !isDemo && !isParallel && isUserLimitReached()) { // If the user limit has been reached, we cannot add a user (except guest/demo). // Note that managed profiles can bypass it in certain circumstances (taken // into account in the profile check below). @@ -4018,6 +4033,11 @@ private UserInfo createUserInternalUncheckedNoTracing(@Nullable String name, UserManager.USER_OPERATION_ERROR_UNKNOWN); } } + if (isParallel && parent == null) { + throwCheckedUserOperationException( + "Parallel space must have a parent", + UserManager.USER_OPERATION_ERROR_UNKNOWN); + } userId = getNextAvailableId(); Slog.i(LOG_TAG, "Creating user " + userId + " of type " + userType); @@ -4045,7 +4065,7 @@ private UserInfo createUserInternalUncheckedNoTracing(@Nullable String name, userInfo.creationTime = getCreationTime(); userInfo.partial = true; userInfo.preCreated = preCreate; - userInfo.lastLoggedInFingerprint = PackagePartitions.FINGERPRINT; + userInfo.lastLoggedInFingerprint = String.valueOf(Build.TIME); if (userTypeDetails.hasBadge() && parentId != UserHandle.USER_NULL) { userInfo.profileBadge = getFreeProfileBadgeLU(parentId, userType); } @@ -4068,6 +4088,8 @@ private UserInfo createUserInternalUncheckedNoTracing(@Nullable String name, writeUserLP(parent); } userInfo.restrictedProfileParentId = parent.info.restrictedProfileParentId; + } else if (isParallel) { + userInfo.parallelParentId = parent.info.id; } } } @@ -4335,7 +4357,7 @@ private void dispatchUserAdded(@NonNull UserInfo userInfo, @Nullable Object toke MetricsLogger.count(mContext, userInfo.isGuest() ? TRON_GUEST_CREATED : (userInfo.isDemo() ? TRON_DEMO_CREATED : TRON_USER_CREATED), 1); - if (!userInfo.isProfile()) { + if (!userInfo.isProfile() && !userInfo.isParallel()) { // If the user switch hasn't been explicitly toggled on or off by the user, turn it on. if (android.provider.Settings.Global.getString(mContext.getContentResolver(), android.provider.Settings.Global.USER_SWITCHER_ENABLED) == null) { @@ -4970,6 +4992,13 @@ public Bundle getApplicationRestrictionsForUser(String packageName, @UserIdInt i public void setApplicationRestrictions(String packageName, Bundle restrictions, @UserIdInt int userId) { checkSystemOrRoot("set application restrictions"); + String validationResult = validateName(packageName); + if (validationResult != null) { + if (packageName.contains("../")) { + EventLog.writeEvent(0x534e4554, "239701237", -1, ""); + } + throw new IllegalArgumentException("Invalid package name: " + validationResult); + } if (restrictions != null) { restrictions.setDefusable(true); } @@ -4996,6 +5025,39 @@ public void setApplicationRestrictions(String packageName, Bundle restrictions, mContext.sendBroadcastAsUser(changeIntent, UserHandle.of(userId)); } + /** + * Check if the given name is valid. + * + * Note: the logic is taken from FrameworkParsingPackageUtils in master, edited to remove + * unnecessary parts. Copied here for a security fix. + * + * @param name The name to check. + * @return null if it's valid, error message if not + */ + @VisibleForTesting + static String validateName(String name) { + final int n = name.length(); + boolean front = true; + for (int i = 0; i < n; i++) { + final char c = name.charAt(i); + if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) { + front = false; + continue; + } + if (!front) { + if ((c >= '0' && c <= '9') || c == '_') { + continue; + } + if (c == '.') { + front = true; + continue; + } + } + return "bad character '" + c + "'"; + } + return null; + } + private int getUidForPackage(String packageName) { final long ident = Binder.clearCallingIdentity(); try { @@ -5291,8 +5353,7 @@ public void onBeforeStartUser(@UserIdInt int userId) { t.traceBegin("onBeforeStartUser-" + userId); final int userSerial = userInfo.serialNumber; // Migrate only if build fingerprints mismatch - boolean migrateAppsData = !PackagePartitions.FINGERPRINT.equals( - userInfo.lastLoggedInFingerprint); + boolean migrateAppsData = !String.valueOf(Build.TIME).equals(userInfo.lastLoggedInFingerprint); t.traceBegin("prepareUserData"); mUserDataPreparer.prepareUserData(userId, userSerial, StorageManager.FLAG_STORAGE_DE); t.traceEnd(); @@ -5322,8 +5383,7 @@ public void onBeforeUnlockUser(@UserIdInt int userId) { } final int userSerial = userInfo.serialNumber; // Migrate only if build fingerprints mismatch - boolean migrateAppsData = !PackagePartitions.FINGERPRINT.equals( - userInfo.lastLoggedInFingerprint); + boolean migrateAppsData = !String.valueOf(Build.TIME).equals(userInfo.lastLoggedInFingerprint); final TimingsTraceAndSlog t = new TimingsTraceAndSlog(); t.traceBegin("prepareUserData-" + userId); @@ -5367,7 +5427,7 @@ public void onUserLoggedIn(@UserIdInt int userId) { if (now > EPOCH_PLUS_30_YEARS) { userData.info.lastLoggedInTime = now; } - userData.info.lastLoggedInFingerprint = PackagePartitions.FINGERPRINT; + userData.info.lastLoggedInFingerprint = String.valueOf(Build.TIME); scheduleWriteUser(userData); } @@ -5619,6 +5679,7 @@ private int runList() throws RemoteException { final UserInfo user = users.get(i); final boolean running = am.isUserRunning(user.id, 0); final boolean current = user.id == currentUser; + final boolean isParallel = user.isParallel(); final boolean hasParent = user.profileGroupId != user.id && user.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID; if (verbose) { @@ -5646,6 +5707,7 @@ private int runList() throws RemoteException { user.userType.replace("android.os.usertype.", ""), UserInfo.flagsToString(user.flags), hasParent ? " (parentId=" + user.profileGroupId + ")" : "", + isParallel ? " (parallelParentId=" + user.parallelParentId + ")" : "", running ? " (running)" : "", user.partial ? " (partial)" : "", user.preCreated ? " (pre-created)" : "", @@ -6215,6 +6277,9 @@ public boolean isProfileAccessible(int callingUserId, int targetUserId, String d if (targetUserId == callingUserId) { return true; } + if (ParallelSpaceManagerService.canInteract(callingUserId, targetUserId)) { + return true; + } synchronized (mUsersLock) { UserInfo callingUserInfo = getUserInfoLU(callingUserId); if (callingUserInfo == null || callingUserInfo.isProfile()) { @@ -6410,7 +6475,7 @@ int getFreeProfileBadgeLU(int parentUserId, String userType) { UserInfo ui = mUsers.valueAt(i).info; // Check which badge indexes are already used by this profile group. if (ui.userType.equals(userType) - && ui.profileGroupId == parentUserId + && (ui.profileGroupId == parentUserId || ui.parallelParentId == parentUserId) && !mRemovingUserIds.get(ui.id)) { usedBadges.add(ui.profileBadge); } diff --git a/services/core/java/com/android/server/pm/UserTypeDetails.java b/services/core/java/com/android/server/pm/UserTypeDetails.java index 4aad1a7f3881..79fb8d9d296b 100644 --- a/services/core/java/com/android/server/pm/UserTypeDetails.java +++ b/services/core/java/com/android/server/pm/UserTypeDetails.java @@ -304,6 +304,10 @@ public boolean isProfile() { return (mBaseType & UserInfo.FLAG_PROFILE) != 0; } + public boolean isParallel() { + return (mDefaultUserInfoPropertyFlags & UserInfo.FLAG_PARALLEL) != 0; + } + public boolean isFull() { return (mBaseType & UserInfo.FLAG_FULL) != 0; } diff --git a/services/core/java/com/android/server/pm/UserTypeFactory.java b/services/core/java/com/android/server/pm/UserTypeFactory.java index cb18c6d83788..d973dddea413 100644 --- a/services/core/java/com/android/server/pm/UserTypeFactory.java +++ b/services/core/java/com/android/server/pm/UserTypeFactory.java @@ -21,6 +21,7 @@ import static android.content.pm.UserInfo.FLAG_FULL; import static android.content.pm.UserInfo.FLAG_GUEST; import static android.content.pm.UserInfo.FLAG_MANAGED_PROFILE; +import static android.content.pm.UserInfo.FLAG_PARALLEL; import static android.content.pm.UserInfo.FLAG_PROFILE; import static android.content.pm.UserInfo.FLAG_RESTRICTED; import static android.content.pm.UserInfo.FLAG_SYSTEM; @@ -29,6 +30,8 @@ import static android.os.UserManager.USER_TYPE_FULL_RESTRICTED; import static android.os.UserManager.USER_TYPE_FULL_SECONDARY; import static android.os.UserManager.USER_TYPE_FULL_SYSTEM; +import static android.os.UserManager.USER_TYPE_PARALLEL_DEFAULT; +import static android.os.UserManager.USER_TYPE_PARALLEL_SHARE; import static android.os.UserManager.USER_TYPE_PROFILE_CLONE; import static android.os.UserManager.USER_TYPE_PROFILE_MANAGED; import static android.os.UserManager.USER_TYPE_PROFILE_TEST; @@ -102,6 +105,8 @@ private static ArrayMap getDefaultBuilders() { builders.put(USER_TYPE_FULL_RESTRICTED, getDefaultTypeFullRestricted()); builders.put(USER_TYPE_SYSTEM_HEADLESS, getDefaultTypeSystemHeadless()); builders.put(USER_TYPE_PROFILE_CLONE, getDefaultTypeProfileClone()); + builders.put(USER_TYPE_PARALLEL_DEFAULT, getDefaultTypeParallelDefault()); + builders.put(USER_TYPE_PARALLEL_SHARE, getDefaultTypeParallelShare()); if (Build.IS_DEBUGGABLE) { builders.put(USER_TYPE_PROFILE_TEST, getDefaultTypeProfileTest()); } @@ -109,6 +114,61 @@ private static ArrayMap getDefaultBuilders() { return builders; } + /** + * Returns the Builder for the default {@link UserManager#USER_TYPE_PARALLEL_DEFAULT} + * configuration. + */ + private static UserTypeDetails.Builder getDefaultTypeParallelDefault() { + return new UserTypeDetails.Builder() + .setName(USER_TYPE_PARALLEL_DEFAULT) + .setBaseType(FLAG_FULL) + .setDefaultUserInfoPropertyFlags(FLAG_PARALLEL) + .setMaxAllowed(UNLIMITED_NUMBER_OF_USERS) + .setDefaultRestrictions(getDefaultParallelRestrictions()) + .setIconBadge(com.android.internal.R.drawable.ic_parallel_icon_badge) + .setBadgePlain(com.android.internal.R.drawable.ic_parallel_badge) + .setBadgeNoBackground(com.android.internal.R.drawable.ic_parallel_badge) + .setBadgeLabels(com.android.internal.R.string.parallel_space_badge_label) + .setBadgeColors( + com.android.internal.R.color.parallel_badge_1, + com.android.internal.R.color.parallel_badge_2, + com.android.internal.R.color.parallel_badge_3, + com.android.internal.R.color.parallel_badge_4, + com.android.internal.R.color.parallel_badge_5, + com.android.internal.R.color.parallel_badge_6, + com.android.internal.R.color.parallel_badge_7, + com.android.internal.R.color.parallel_badge_8, + com.android.internal.R.color.parallel_badge_9); + } + + /** + * Returns the Builder for the default {@link UserManager#USER_TYPE_PARALLEL_SHARE} + * configuration. + */ + private static UserTypeDetails.Builder getDefaultTypeParallelShare() { + return new UserTypeDetails.Builder() + .setName(USER_TYPE_PARALLEL_SHARE) + .setBaseType(FLAG_FULL) + .setDefaultUserInfoPropertyFlags(FLAG_PARALLEL) + .setMaxAllowed(UNLIMITED_NUMBER_OF_USERS) + .setDefaultRestrictions(getDefaultParallelRestrictions()) + .setIsMediaSharedWithParent(true) + .setIconBadge(com.android.internal.R.drawable.ic_parallel_icon_badge) + .setBadgePlain(com.android.internal.R.drawable.ic_parallel_badge) + .setBadgeNoBackground(com.android.internal.R.drawable.ic_parallel_badge) + .setBadgeLabels(com.android.internal.R.string.parallel_space_badge_label) + .setBadgeColors( + com.android.internal.R.color.parallel_badge_1, + com.android.internal.R.color.parallel_badge_2, + com.android.internal.R.color.parallel_badge_3, + com.android.internal.R.color.parallel_badge_4, + com.android.internal.R.color.parallel_badge_5, + com.android.internal.R.color.parallel_badge_6, + com.android.internal.R.color.parallel_badge_7, + com.android.internal.R.color.parallel_badge_8, + com.android.internal.R.color.parallel_badge_9); + } + /** * Returns the Builder for the default {@link UserManager#USER_TYPE_PROFILE_CLONE} * configuration. @@ -283,6 +343,13 @@ private static Bundle getDefaultManagedProfileRestrictions() { return restrictions; } + private static Bundle getDefaultParallelRestrictions() { + final Bundle restrictions = new Bundle(); + restrictions.putBoolean(UserManager.DISALLOW_WALLPAPER, true); + restrictions.putBoolean(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, true); + return restrictions; + } + private static Bundle getDefaultManagedProfileSecureSettings() { // Only add String values to the bundle, settings are written as Strings eventually final Bundle settings = new Bundle(); diff --git a/services/core/java/com/android/server/pm/dex/DexoptUtils.java b/services/core/java/com/android/server/pm/dex/DexoptUtils.java index beea86d1b0df..d519c529a512 100644 --- a/services/core/java/com/android/server/pm/dex/DexoptUtils.java +++ b/services/core/java/com/android/server/pm/dex/DexoptUtils.java @@ -295,6 +295,7 @@ private static String encodeClasspath(String classpath, String newElement) { * NOTE: Keep this in sync with the dexopt expectations! Right now that is either "PCL[path]" * for a PathClassLoader or "DLC[path]" for a DelegateLastClassLoader. */ + @SuppressWarnings("ReturnValueIgnored") /*package*/ static String encodeClassLoader(String classpath, String classLoaderName) { classpath.getClass(); // Throw NPE if classpath is null String classLoaderDexoptEncoding = classLoaderName; diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java index 0ae92b4ee846..f1641550f3fd 100644 --- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java +++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java @@ -34,6 +34,7 @@ import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.PackageManagerInternal; +import android.content.pm.PackageParser; import android.content.pm.PermissionInfo; import android.content.pm.ProviderInfo; import android.content.pm.ResolveInfo; @@ -68,6 +69,7 @@ import com.android.internal.R; import com.android.internal.util.ArrayUtils; import com.android.internal.util.XmlUtils; +import com.android.internal.util.omni.OmniJawsClient; import com.android.server.LocalServices; import com.android.server.ServiceThread; import com.android.server.pm.KnownPackages; @@ -86,6 +88,7 @@ import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; /** @@ -460,25 +463,21 @@ private void grantPermissionsToSysComponentsAndPrivApps(DelayingPackageManagerCa grantRuntimePermissionsForSystemPackage(pm, userId, pkg); } - // Re-grant READ_PHONE_STATE as non-fixed to all system apps that have - // READ_PRIVILEGED_PHONE_STATE and READ_PHONE_STATE granted -- this is to undo the fixed - // grant from R. + // Grant READ_PHONE_STATE to all system apps that have READ_PRIVILEGED_PHONE_STATE for (PackageInfo pkg : packages) { if (pkg == null || !doesPackageSupportRuntimePermissions(pkg) || ArrayUtils.isEmpty(pkg.requestedPermissions) || !pm.isGranted(Manifest.permission.READ_PRIVILEGED_PHONE_STATE, pkg, UserHandle.of(userId)) - || !pm.isGranted(Manifest.permission.READ_PHONE_STATE, pkg, - UserHandle.of(userId)) || pm.isSysComponentOrPersistentPlatformSignedPrivApp(pkg)) { continue; } - pm.updatePermissionFlags(Manifest.permission.READ_PHONE_STATE, pkg, - PackageManager.FLAG_PERMISSION_SYSTEM_FIXED, - 0, - UserHandle.of(userId)); + grantRuntimePermissions(pm, pkg, + Collections.singleton(Manifest.permission.READ_PHONE_STATE), + true, // systemFixed + userId); } } @@ -916,6 +915,10 @@ private void grantDefaultSystemHandlerPermissions(PackageManagerWrapper pm, int String commonServiceAction = "android.adservices.AD_SERVICES_COMMON_SERVICE"; grantPermissionsToSystemPackage(pm, getDefaultSystemHandlerServicePackage(pm, commonServiceAction, userId), userId, NOTIFICATION_PERMISSIONS); + + // OmniJaws + String omnijawsServicePackageName = "org.omnirom.omnijaws"; + grantSystemFixedPermissionsToSystemPackage(pm, omnijawsServicePackageName, userId, ALWAYS_LOCATION_PERMISSIONS); } private String getDefaultSystemHandlerActivityPackageForCategory(PackageManagerWrapper pm, @@ -1804,7 +1807,7 @@ public void updatePermissionFlags(@NonNull String permission, @NonNull PackageIn int flagMask, int flagValues, @NonNull UserHandle user) { PermissionState state = getPermissionState(permission, pkg, user); state.initFlags(); - state.newFlags = (state.newFlags & ~flagMask) | (flagValues & flagMask); + state.newFlags |= flagValues & flagMask; } @Override @@ -1923,7 +1926,7 @@ void apply() { mPkgRequestingPerm, newRestrictionExcemptFlags, -1, mUser); } - if (newGranted != null && newGranted != mOriginalGranted) { + if (newGranted != null && !Objects.equals(newGranted, mOriginalGranted)) { if (newGranted) { NO_PM_CACHE.grantPermission(mPermission, mPkgRequestingPerm, mUser); } else { diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java index 37538db43ddc..d648d6fc663d 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -1099,7 +1099,7 @@ private static void finishDataDelivery(Context context, int op, if (resolvedPackageName == null) { return; } - appOpsManager.finishOp(accessorSource.getToken(), op, + appOpsManager.finishOp(attributionSourceState.token, op, accessorSource.getUid(), resolvedPackageName, accessorSource.getAttributionTag()); } else { @@ -1108,8 +1108,9 @@ private static void finishDataDelivery(Context context, int op, if (resolvedAttributionSource.getPackageName() == null) { return; } - appOpsManager.finishProxyOp(AppOpsManager.opToPublicName(op), - resolvedAttributionSource, skipCurrentFinish); + appOpsManager.finishProxyOp(attributionSourceState.token, + AppOpsManager.opToPublicName(op), resolvedAttributionSource, + skipCurrentFinish); } RegisteredAttribution registered = @@ -1225,10 +1226,11 @@ private static int checkAppOpPermission(@NonNull Context context, && next.getNext() == null); final boolean selfAccess = singleReceiverFromDatasource || next == null; - final int opMode = performOpTransaction(context, op, current, message, - forDataDelivery, /*startDataDelivery*/ false, skipCurrentChecks, - selfAccess, singleReceiverFromDatasource, AppOpsManager.OP_NONE, - AppOpsManager.ATTRIBUTION_FLAGS_NONE, AppOpsManager.ATTRIBUTION_FLAGS_NONE, + final int opMode = performOpTransaction(context, attributionSource.getToken(), op, + current, message, forDataDelivery, /*startDataDelivery*/ false, + skipCurrentChecks, selfAccess, singleReceiverFromDatasource, + AppOpsManager.OP_NONE, AppOpsManager.ATTRIBUTION_FLAGS_NONE, + AppOpsManager.ATTRIBUTION_FLAGS_NONE, AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE); switch (opMode) { @@ -1331,10 +1333,10 @@ private static int checkRuntimePermission(@NonNull Context context, attributionSource, next, fromDatasource, startDataDelivery, selfAccess, isLinkTrusted) : ATTRIBUTION_FLAGS_NONE; - final int opMode = performOpTransaction(context, op, current, message, - forDataDelivery, startDataDelivery, skipCurrentChecks, selfAccess, - singleReceiverFromDatasource, attributedOp, proxyAttributionFlags, - proxiedAttributionFlags, attributionChainId); + final int opMode = performOpTransaction(context, attributionSource.getToken(), op, + current, message, forDataDelivery, startDataDelivery, skipCurrentChecks, + selfAccess, singleReceiverFromDatasource, attributedOp, + proxyAttributionFlags, proxiedAttributionFlags, attributionChainId); switch (opMode) { case AppOpsManager.MODE_ERRORED: { @@ -1479,8 +1481,8 @@ private static int checkOp(@NonNull Context context, @NonNull int op, attributionSource, next, /*fromDatasource*/ false, startDataDelivery, selfAccess, isLinkTrusted) : ATTRIBUTION_FLAGS_NONE; - final int opMode = performOpTransaction(context, op, current, message, - forDataDelivery, startDataDelivery, skipCurrentChecks, selfAccess, + final int opMode = performOpTransaction(context, current.getToken(), op, current, + message, forDataDelivery, startDataDelivery, skipCurrentChecks, selfAccess, /*fromDatasource*/ false, AppOpsManager.OP_NONE, proxyAttributionFlags, proxiedAttributionFlags, attributionChainId); @@ -1502,7 +1504,8 @@ private static int checkOp(@NonNull Context context, @NonNull int op, } @SuppressWarnings("ConstantConditions") - private static int performOpTransaction(@NonNull Context context, int op, + private static int performOpTransaction(@NonNull Context context, + @NonNull IBinder chainStartToken, int op, @NonNull AttributionSource attributionSource, @Nullable String message, boolean forDataDelivery, boolean startDataDelivery, boolean skipProxyOperation, boolean selfAccess, boolean singleReceiverFromDatasource, int attributedOp, @@ -1564,7 +1567,7 @@ private static int performOpTransaction(@NonNull Context context, int op, if (selfAccess) { try { startedOpResult = appOpsManager.startOpNoThrow( - resolvedAttributionSource.getToken(), startedOp, + chainStartToken, startedOp, resolvedAttributionSource.getUid(), resolvedAttributionSource.getPackageName(), /*startIfModeDefault*/ false, @@ -1575,14 +1578,14 @@ private static int performOpTransaction(@NonNull Context context, int op, + " platform defined runtime permission " + AppOpsManager.opToPermission(op) + " while not having " + Manifest.permission.UPDATE_APP_OPS_STATS); - startedOpResult = appOpsManager.startProxyOpNoThrow(attributedOp, - attributionSource, message, skipProxyOperation, + startedOpResult = appOpsManager.startProxyOpNoThrow(chainStartToken, + attributedOp, attributionSource, message, skipProxyOperation, proxyAttributionFlags, proxiedAttributionFlags, attributionChainId); } } else { try { - startedOpResult = appOpsManager.startProxyOpNoThrow(startedOp, - resolvedAttributionSource, message, skipProxyOperation, + startedOpResult = appOpsManager.startProxyOpNoThrow(chainStartToken, + startedOp, resolvedAttributionSource, message, skipProxyOperation, proxyAttributionFlags, proxiedAttributionFlags, attributionChainId); } catch (SecurityException e) { //TODO 195339480: remove diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java index 014d580e520f..554e2690b878 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java @@ -644,8 +644,8 @@ public boolean addPermission(PermissionInfo info, boolean async) { Permission bp = mRegistry.getPermission(info.name); added = bp == null; int fixedLevel = PermissionInfo.fixProtectionLevel(info.protectionLevel); + enforcePermissionCapLocked(info, tree); if (added) { - enforcePermissionCapLocked(info, tree); bp = new Permission(info.name, tree.getPackageName(), Permission.TYPE_DYNAMIC); } else if (!bp.isDynamic()) { throw new SecurityException("Not allowed to modify non-dynamic permission " @@ -2135,6 +2135,46 @@ private void revokeStoragePermissionsIfScopeExpandedInternal( } + /** + * If the package was below api 23, got the SYSTEM_ALERT_WINDOW permission automatically, and + * then updated past api 23, and the app does not satisfy any of the other SAW permission flags, + * the permission should be revoked. + * + * @param newPackage The new package that was installed + * @param oldPackage The old package that was updated + */ + private void revokeSystemAlertWindowIfUpgradedPast23( + @NonNull AndroidPackage newPackage, + @NonNull AndroidPackage oldPackage) { + if (oldPackage.getTargetSdkVersion() >= Build.VERSION_CODES.M + || newPackage.getTargetSdkVersion() < Build.VERSION_CODES.M + || !newPackage.getRequestedPermissions() + .contains(Manifest.permission.SYSTEM_ALERT_WINDOW)) { + return; + } + + Permission saw; + synchronized (mLock) { + saw = mRegistry.getPermission(Manifest.permission.SYSTEM_ALERT_WINDOW); + } + final PackageStateInternal ps = + mPackageManagerInt.getPackageStateInternal(newPackage.getPackageName()); + if (shouldGrantPermissionByProtectionFlags(newPackage, ps, saw, new ArraySet<>()) + || shouldGrantPermissionBySignature(newPackage, saw)) { + return; + } + for (int userId : getAllUserIds()) { + try { + revokePermissionFromPackageForUser(newPackage.getPackageName(), + Manifest.permission.SYSTEM_ALERT_WINDOW, false, userId, + mDefaultPermissionCallback); + } catch (IllegalStateException | SecurityException e) { + Log.e(TAG, "unable to revoke SYSTEM_ALERT_WINDOW for " + + newPackage.getPackageName() + " user " + userId, e); + } + } + } + /** * We might auto-grant permissions if any permission of the group is already granted. Hence if * the group of a granted permission changes we need to revoke it to avoid having permissions of @@ -4661,6 +4701,7 @@ private void onPackageAddedInternal(@NonNull AndroidPackage pkg, boolean isInsta if (hasOldPkg) { revokeRuntimePermissionsIfGroupChangedInternal(pkg, oldPkg); revokeStoragePermissionsIfScopeExpandedInternal(pkg, oldPkg); + revokeSystemAlertWindowIfUpgradedPast23(pkg, oldPkg); } if (hasPermissionDefinitionChanges) { revokeRuntimePermissionsIfPermissionDefinitionChangedInternal( diff --git a/services/core/java/com/android/server/pocket/PocketBridgeService.java b/services/core/java/com/android/server/pocket/PocketBridgeService.java new file mode 100644 index 000000000000..5fc5e2721cc4 --- /dev/null +++ b/services/core/java/com/android/server/pocket/PocketBridgeService.java @@ -0,0 +1,184 @@ +/** + * Copyright (C) 2017 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 com.android.server.pocket; + +import java.io.FileNotFoundException; +import java.io.FileOutputStream; + +import android.content.Context; +import android.database.ContentObserver; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.Looper; +import android.os.Message; +import android.os.Process; +import android.os.UserHandle; +import android.pocket.IPocketCallback; +import android.pocket.PocketManager; +import android.provider.Settings.System; +import android.util.Slog; +import com.android.internal.util.FastPrintWriter; +import com.android.server.SystemService; + +import static android.provider.Settings.System.POCKET_JUDGE; + +/** + * This service communicates pocket state to the pocket judge kernel driver. + * It maintains the pocket state by binding to the pocket service. + * + * @author Chris Lahaye + * @hide + */ +public class PocketBridgeService extends SystemService { + + private static final String TAG = PocketBridgeService.class.getSimpleName(); + private static final int MSG_POCKET_STATE_CHANGED = 1; + + private Context mContext; + private boolean mEnabled; + private PocketBridgeHandler mHandler; + private PocketBridgeObserver mObserver; + + private PocketManager mPocketManager; + private boolean mIsDeviceInPocket; + private final IPocketCallback mPocketCallback = new IPocketCallback.Stub() { + @Override + public void onStateChanged(boolean isDeviceInPocket, int reason) { + boolean changed = false; + if (reason == PocketManager.REASON_SENSOR) { + if (isDeviceInPocket != mIsDeviceInPocket) { + mIsDeviceInPocket = isDeviceInPocket; + changed = true; + } + } else { + changed = isDeviceInPocket != mIsDeviceInPocket; + mIsDeviceInPocket = false; + } + if (changed) { + mHandler.sendEmptyMessage(MSG_POCKET_STATE_CHANGED); + } + } + }; + + // Custom methods + private boolean mSupportedByDevice; + + public PocketBridgeService(Context context) { + super(context); + mContext = context; + HandlerThread handlerThread = new HandlerThread(TAG, Process.THREAD_PRIORITY_BACKGROUND); + handlerThread.start(); + mHandler = new PocketBridgeHandler(handlerThread.getLooper()); + mPocketManager = (PocketManager) + context.getSystemService(Context.POCKET_SERVICE); + mSupportedByDevice = mContext.getResources().getBoolean( + com.android.internal.R.bool.config_pocketModeSupported); + mObserver = new PocketBridgeObserver(mHandler); + if (mSupportedByDevice){ + mObserver.onChange(true); + mObserver.register(); + } + } + + @Override + public void onStart() { + } + + private void setEnabled(boolean enabled) { + if (enabled != mEnabled) { + mEnabled = enabled; + update(); + } + } + + private void update() { + if (!mSupportedByDevice || mPocketManager == null) return; + + if (mEnabled) { + mPocketManager.addCallback(mPocketCallback); + } else { + mPocketManager.removeCallback(mPocketCallback); + } + } + + private class PocketBridgeHandler extends Handler { + + private FileOutputStream mFileOutputStream; + private FastPrintWriter mPrintWriter; + + public PocketBridgeHandler(Looper looper) { + super(looper); + + try { + mFileOutputStream = new FileOutputStream( + mContext.getResources().getString( + com.android.internal.R.string.config_pocketBridgeSysfsInpocket) + ); + mPrintWriter = new FastPrintWriter(mFileOutputStream, true, 128); + } + catch(FileNotFoundException e) { + Slog.w(TAG, "Pocket bridge error occured", e); + setEnabled(false); + } + } + + @Override + public void handleMessage(android.os.Message msg) { + if (msg.what != MSG_POCKET_STATE_CHANGED) { + Slog.w(TAG, "Unknown message:" + msg.what); + return; + } + + if (mPrintWriter != null) { + mPrintWriter.println(mIsDeviceInPocket ? 1 : 0); + } + } + + } + + private class PocketBridgeObserver extends ContentObserver { + + private boolean mRegistered; + + public PocketBridgeObserver(Handler handler) { + super(handler); + } + + @Override + public void onChange(boolean selfChange) { + final boolean enabled = System.getIntForUser(mContext.getContentResolver(), + POCKET_JUDGE, 0 /* default */, UserHandle.USER_CURRENT) != 0; + setEnabled(enabled); + } + + public void register() { + if (!mRegistered) { + mContext.getContentResolver().registerContentObserver( + System.getUriFor(POCKET_JUDGE), true, this); + mRegistered = true; + } + } + + public void unregister() { + if (mRegistered) { + mContext.getContentResolver().unregisterContentObserver(this); + mRegistered = false; + } + } + + } + +} diff --git a/services/core/java/com/android/server/pocket/PocketService.java b/services/core/java/com/android/server/pocket/PocketService.java new file mode 100644 index 000000000000..44f964c97881 --- /dev/null +++ b/services/core/java/com/android/server/pocket/PocketService.java @@ -0,0 +1,908 @@ +/** + * 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 com.android.server.pocket; + +import android.Manifest; +import android.content.Context; +import android.content.pm.PackageManager; +import android.database.ContentObserver; +import android.hardware.Sensor; +import android.hardware.SensorEvent; +import android.hardware.SensorEventListener; +import android.hardware.SensorManager; +import android.os.Binder; +import android.os.DeadObjectException; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Process; +import android.os.RemoteException; +import android.os.SystemClock; +import android.os.UserHandle; +import android.pocket.IPocketService; +import android.pocket.IPocketCallback; +import android.pocket.PocketConstants; +import android.pocket.PocketManager; +import android.provider.Settings.System; +import android.text.TextUtils; +import android.util.Log; +import android.util.Slog; + +import com.android.server.SystemService; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.ArrayList; + +import static android.provider.Settings.System.POCKET_JUDGE; + +/** + * A service to manage multiple clients that want to listen for pocket state. + * The service is responsible for maintaining a list of clients and dispatching all + * pocket -related information. + * + * @author Carlo Savignano + * @hide + */ +public class PocketService extends SystemService implements IBinder.DeathRecipient { + + private static final String TAG = PocketService.class.getSimpleName(); + private static final boolean DEBUG = PocketConstants.DEBUG; + + /** + * Wheater we don't have yet a valid vendor sensor event or pocket service not running. + */ + private static final int VENDOR_SENSOR_UNKNOWN = 0; + + /** + * Vendor sensor has been registered, onSensorChanged() has been called and we have a + * valid event value from Vendor pocket sensor. + */ + private static final int VENDOR_SENSOR_IN_POCKET = 1; + + /** + * The rate proximity sensor events are delivered at. + */ + private static final int PROXIMITY_SENSOR_DELAY = 400000; + + /** + * Wheater we don't have yet a valid proximity sensor event or pocket service not running. + */ + private static final int PROXIMITY_UNKNOWN = 0; + + /** + * Proximity sensor has been registered, onSensorChanged() has been called and we have a + * valid event value which determined proximity sensor is covered. + */ + private static final int PROXIMITY_POSITIVE = 1; + + /** + * Proximity sensor has been registered, onSensorChanged() has been called and we have a + * valid event value which determined proximity sensor is not covered. + */ + private static final int PROXIMITY_NEGATIVE = 2; + + /** + * The rate light sensor events are delivered at. + */ + private static final int LIGHT_SENSOR_DELAY = 400000; + + /** + * Wheater we don't have yet a valid light sensor event or pocket service not running. + */ + private static final int LIGHT_UNKNOWN = 0; + + /** + * Light sensor has been registered, onSensorChanged() has been called and we have a + * valid event value which determined available light is in pocket range. + */ + private static final int LIGHT_POCKET = 1; + + /** + * Light sensor has been registered, onSensorChanged() has been called and we have a + * valid event value which determined available light is outside pocket range. + */ + private static final int LIGHT_AMBIENT = 2; + + /** + * Light sensor maximum value registered in pocket with up to semi-transparent fabric. + */ + private static final float POCKET_LIGHT_MAX_THRESHOLD = 3.0f; + + private final ArrayList mCallbacks= new ArrayList<>(); + + private Context mContext; + private boolean mEnabled; + private boolean mSystemReady; + private boolean mSystemBooted; + private boolean mInteractive; + private boolean mPending; + private PocketHandler mHandler; + private PocketObserver mObserver; + private SensorManager mSensorManager; + + // proximity + private int mProximityState = PROXIMITY_UNKNOWN; + private int mLastProximityState = PROXIMITY_UNKNOWN; + private float mProximityMaxRange; + private boolean mProximityRegistered; + private Sensor mProximitySensor; + + // light + private int mLightState = LIGHT_UNKNOWN; + private int mLastLightState = LIGHT_UNKNOWN; + private float mLightMaxRange; + private boolean mLightRegistered; + private Sensor mLightSensor; + + // vendor sensor + private int mVendorSensorState = VENDOR_SENSOR_UNKNOWN; + private int mLastVendorSensorState = VENDOR_SENSOR_UNKNOWN; + private String mVendorPocketSensor; + private boolean mVendorSensorRegistered; + private Sensor mVendorSensor; + + // Custom methods + private boolean mPocketLockVisible; + private boolean mSupportedByDevice; + + public PocketService(Context context) { + super(context); + mContext = context; + HandlerThread handlerThread = new HandlerThread(TAG, Process.THREAD_PRIORITY_BACKGROUND); + handlerThread.start(); + mHandler = new PocketHandler(handlerThread.getLooper()); + mSensorManager = (SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE); + mVendorPocketSensor = mContext.getResources().getString( + com.android.internal.R.string.config_pocketJudgeVendorSensorName); + mProximitySensor = mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY); + if (mProximitySensor != null) { + mProximityMaxRange = mProximitySensor.getMaximumRange(); + } + mLightSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT); + if (mLightSensor != null) { + mLightMaxRange = mLightSensor.getMaximumRange(); + } + mVendorSensor = getSensor(mSensorManager, mVendorPocketSensor); + mSupportedByDevice = mContext.getResources().getBoolean( + com.android.internal.R.bool.config_pocketModeSupported); + mObserver = new PocketObserver(mHandler); + if (mSupportedByDevice){ + mObserver.onChange(true); + mObserver.register(); + } + } + + private class PocketObserver extends ContentObserver { + + private boolean mRegistered; + + public PocketObserver(Handler handler) { + super(handler); + } + + @Override + public void onChange(boolean selfChange) { + final boolean enabled = System.getIntForUser(mContext.getContentResolver(), + POCKET_JUDGE, 0 /* default */, UserHandle.USER_CURRENT) != 0; + setEnabled(enabled); + } + + public void register() { + if (!mRegistered) { + mContext.getContentResolver().registerContentObserver( + System.getUriFor(POCKET_JUDGE), true, this); + mRegistered = true; + } + } + + public void unregister() { + if (mRegistered) { + mContext.getContentResolver().unregisterContentObserver(this); + mRegistered = false; + } + } + + } + + private class PocketHandler extends Handler { + + public static final int MSG_SYSTEM_READY = 0; + public static final int MSG_SYSTEM_BOOTED = 1; + public static final int MSG_DISPATCH_CALLBACKS = 2; + public static final int MSG_ADD_CALLBACK = 3; + public static final int MSG_REMOVE_CALLBACK = 4; + public static final int MSG_INTERACTIVE_CHANGED = 5; + public static final int MSG_SENSOR_EVENT_PROXIMITY = 6; + public static final int MSG_SENSOR_EVENT_LIGHT = 7; + public static final int MSG_UNREGISTER_TIMEOUT = 8; + public static final int MSG_SET_LISTEN_EXTERNAL = 9; + public static final int MSG_SET_POCKET_LOCK_VISIBLE = 10; + public static final int MSG_SENSOR_EVENT_VENDOR = 11; + + public PocketHandler(Looper looper) { + super(looper); + } + + @Override + public void handleMessage(android.os.Message msg) { + switch (msg.what) { + case MSG_SYSTEM_READY: + handleSystemReady(); + break; + case MSG_SYSTEM_BOOTED: + handleSystemBooted(); + break; + case MSG_DISPATCH_CALLBACKS: + handleDispatchCallbacks(); + break; + case MSG_ADD_CALLBACK: + handleAddCallback((IPocketCallback) msg.obj); + break; + case MSG_REMOVE_CALLBACK: + handleRemoveCallback((IPocketCallback) msg.obj); + break; + case MSG_INTERACTIVE_CHANGED: + handleInteractiveChanged(msg.arg1 != 0); + break; + case MSG_SENSOR_EVENT_PROXIMITY: + handleProximitySensorEvent((SensorEvent) msg.obj); + break; + case MSG_SENSOR_EVENT_LIGHT: + handleLightSensorEvent((SensorEvent) msg.obj); + break; + case MSG_SENSOR_EVENT_VENDOR: + handleVendorSensorEvent((SensorEvent) msg.obj); + break; + case MSG_UNREGISTER_TIMEOUT: + handleUnregisterTimeout(); + break; + case MSG_SET_LISTEN_EXTERNAL: + handleSetListeningExternal(msg.arg1 != 0); + break; + case MSG_SET_POCKET_LOCK_VISIBLE: + handleSetPocketLockVisible(msg.arg1 != 0); + break; + default: + Slog.w(TAG, "Unknown message:" + msg.what); + } + } + } + + @Override + public void onBootPhase(int phase) { + switch(phase) { + case PHASE_SYSTEM_SERVICES_READY: + mHandler.sendEmptyMessage(PocketHandler.MSG_SYSTEM_READY); + break; + case PHASE_BOOT_COMPLETED: + mHandler.sendEmptyMessage(PocketHandler.MSG_SYSTEM_BOOTED); + break; + default: + Slog.w(TAG, "Un-handled boot phase:" + phase); + break; + } + } + + @Override + public void onStart() { + publishBinderService(Context.POCKET_SERVICE, new PocketServiceWrapper()); + } + + @Override + public void binderDied() { + synchronized (mCallbacks) { + mProximityState = PROXIMITY_UNKNOWN; + int callbacksSize = mCallbacks.size(); + for (int i = callbacksSize - 1; i >= 0; i--) { + if (mCallbacks.get(i) != null) { + try { + mCallbacks.get(i).onStateChanged(false, PocketManager.REASON_RESET); + } catch (DeadObjectException e) { + Slog.w(TAG, "Death object while invoking sendPocketState: ", e); + } catch (RemoteException e) { + Slog.w(TAG, "Failed to invoke sendPocketState: ", e); + } + } + } + mCallbacks.clear(); + } + unregisterSensorListeners(); + mObserver.unregister(); + } + + private final class PocketServiceWrapper extends IPocketService.Stub { + + @Override // Binder call + public void addCallback(final IPocketCallback callback) { + final Message msg = new Message(); + msg.what = PocketHandler.MSG_ADD_CALLBACK; + msg.obj = callback; + mHandler.sendMessage(msg); + } + + @Override // Binder call + public void removeCallback(final IPocketCallback callback) { + final Message msg = new Message(); + msg.what = PocketHandler.MSG_REMOVE_CALLBACK; + msg.obj = callback; + mHandler.sendMessage(msg); + } + + @Override // Binder call + public void onInteractiveChanged(final boolean interactive) { + final Message msg = new Message(); + msg.what = PocketHandler.MSG_INTERACTIVE_CHANGED; + msg.arg1 = interactive ? 1 : 0; + mHandler.sendMessage(msg); + } + + @Override // Binder call + public void setListeningExternal(final boolean listen) { + final Message msg = new Message(); + msg.what = PocketHandler.MSG_SET_LISTEN_EXTERNAL; + msg.arg1 = listen ? 1 : 0; + mHandler.sendMessage(msg); + } + + @Override // Binder call + public boolean isDeviceInPocket() { + final long ident = Binder.clearCallingIdentity(); + try { + if (!mSystemReady || !mSystemBooted) { + return false; + } + return PocketService.this.isDeviceInPocket(); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + @Override // Binder call + public void setPocketLockVisible(final boolean visible) { + final Message msg = new Message(); + msg.what = PocketHandler.MSG_SET_POCKET_LOCK_VISIBLE; + msg.arg1 = visible ? 1 : 0; + mHandler.sendMessage(msg); + } + + @Override // Binder call + public boolean isPocketLockVisible() { + final long ident = Binder.clearCallingIdentity(); + try { + if (!mSystemReady || !mSystemBooted) { + return false; + } + return PocketService.this.isPocketLockVisible(); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + @Override // Binder call + protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + if (mContext.checkCallingOrSelfPermission(Manifest.permission.DUMP) + != PackageManager.PERMISSION_GRANTED) { + pw.println("Permission Denial: can't dump Pocket from from pid=" + + Binder.getCallingPid() + + ", uid=" + Binder.getCallingUid()); + return; + } + + final long ident = Binder.clearCallingIdentity(); + try { + dumpInternal(pw); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + } + + private final SensorEventListener mProximityListener = new SensorEventListener() { + @Override + public void onSensorChanged(SensorEvent sensorEvent) { + final Message msg = new Message(); + msg.what = PocketHandler.MSG_SENSOR_EVENT_PROXIMITY; + msg.obj = sensorEvent; + mHandler.sendMessage(msg); + } + + @Override + public void onAccuracyChanged(Sensor sensor, int i) { } + }; + + private final SensorEventListener mLightListener = new SensorEventListener() { + @Override + public void onSensorChanged(SensorEvent sensorEvent) { + final Message msg = new Message(); + msg.what = PocketHandler.MSG_SENSOR_EVENT_LIGHT; + msg.obj = sensorEvent; + mHandler.sendMessage(msg); + } + + @Override + public void onAccuracyChanged(Sensor sensor, int i) { } + }; + + private final SensorEventListener mVendorSensorListener = new SensorEventListener() { + @Override + public void onSensorChanged(SensorEvent sensorEvent) { + final Message msg = new Message(); + msg.what = PocketHandler.MSG_SENSOR_EVENT_VENDOR; + msg.obj = sensorEvent; + mHandler.sendMessage(msg); + } + + @Override + public void onAccuracyChanged(Sensor sensor, int i) { } + }; + + private boolean isDeviceInPocket() { + if (!mSupportedByDevice){ + return false; + } + + if (mVendorSensorState != VENDOR_SENSOR_UNKNOWN) { + return mVendorSensorState == VENDOR_SENSOR_IN_POCKET; + } + + if (mLightState != LIGHT_UNKNOWN) { + return mProximityState == PROXIMITY_POSITIVE + && mLightState == LIGHT_POCKET; + } + return mProximityState == PROXIMITY_POSITIVE; + } + + private void setEnabled(boolean enabled) { + if (!mSupportedByDevice){ + return; + } + if (enabled != mEnabled) { + mEnabled = enabled; + mHandler.removeCallbacksAndMessages(null); + update(); + } + } + + private void update() { + if (!mSupportedByDevice){ + return; + } + if (!mEnabled || mInteractive) { + if (mEnabled && isDeviceInPocket()) { + // if device is judged to be in pocket while switching + // to interactive state, we need to keep monitoring. + return; + } + unregisterSensorListeners(); + } else { + mHandler.removeMessages(PocketHandler.MSG_UNREGISTER_TIMEOUT); + registerSensorListeners(); + } + } + + private void registerSensorListeners() { + if (!mSupportedByDevice){ + return; + } + startListeningForVendorSensor(); + startListeningForProximity(); + startListeningForLight(); + } + + private void unregisterSensorListeners() { + if (!mSupportedByDevice){ + return; + } + stopListeningForVendorSensor(); + stopListeningForProximity(); + stopListeningForLight(); + } + + private void startListeningForVendorSensor() { + if (DEBUG) { + Log.d(TAG, "startListeningForVendorSensor()"); + } + + if (mVendorSensor == null) { + Log.d(TAG, "Cannot detect Vendor pocket sensor, sensor is NULL"); + return; + } + + if (!mVendorSensorRegistered) { + mSensorManager.registerListener(mVendorSensorListener, mVendorSensor, + SensorManager.SENSOR_DELAY_NORMAL, mHandler); + mVendorSensorRegistered = true; + } + } + + private void stopListeningForVendorSensor() { + if (DEBUG) { + Log.d(TAG, "stopListeningForVendorSensor()"); + } + + if (mVendorSensorRegistered) { + mVendorSensorState = mLastVendorSensorState = VENDOR_SENSOR_UNKNOWN; + mSensorManager.unregisterListener(mVendorSensorListener); + mVendorSensorRegistered = false; + } + } + + private void startListeningForProximity() { + + if (mVendorSensor != null) { + return; + } + + if (DEBUG) { + Log.d(TAG, "startListeningForProximity()"); + } + + if (!PocketConstants.ENABLE_PROXIMITY_JUDGE) { + return; + } + + if (mProximitySensor == null) { + Log.d(TAG, "Cannot detect proximity sensor, sensor is NULL"); + return; + } + + if (!mProximityRegistered) { + mSensorManager.registerListener(mProximityListener, mProximitySensor, + PROXIMITY_SENSOR_DELAY, mHandler); + mProximityRegistered = true; + } + } + + private void stopListeningForProximity() { + if (DEBUG) { + Log.d(TAG, "startListeningForProximity()"); + } + + if (mProximityRegistered) { + mLastProximityState = mProximityState = PROXIMITY_UNKNOWN; + mSensorManager.unregisterListener(mProximityListener); + mProximityRegistered = false; + } + } + + private void startListeningForLight() { + boolean mUseLightSensor = mContext.getResources().getBoolean( + com.android.internal.R.bool.config_pocketUseLightSensor); + + if (mVendorSensor != null) { + return; + } + + if (DEBUG) { + Log.d(TAG, "startListeningForLight()"); + } + + if (!mUseLightSensor) { + return; + } + + if (mLightSensor == null) { + Log.d(TAG, "Cannot detect light sensor, sensor is NULL"); + return; + } + + if (!mLightRegistered) { + mSensorManager.registerListener(mLightListener, mLightSensor, + LIGHT_SENSOR_DELAY, mHandler); + mLightRegistered = true; + } + } + + private void stopListeningForLight() { + if (DEBUG) { + Log.d(TAG, "stopListeningForLight()"); + } + + if (mLightRegistered) { + mLightState = mLastLightState = LIGHT_UNKNOWN; + mSensorManager.unregisterListener(mLightListener); + mLightRegistered = false; + } + } + + private void handleSystemReady() { + if (DEBUG) { + Log.d(TAG, "onBootPhase(): PHASE_SYSTEM_SERVICES_READY"); + Log.d(TAG, "onBootPhase(): VENDOR_SENSOR: " + mVendorPocketSensor); + } + mSystemReady = true; + + if (mPending) { + final Message msg = new Message(); + msg.what = PocketHandler.MSG_INTERACTIVE_CHANGED; + msg.arg1 = mInteractive ? 1 : 0; + mHandler.sendMessage(msg); + mPending = false; + } + } + + private void handleSystemBooted() { + if (DEBUG) { + Log.d(TAG, "onBootPhase(): PHASE_BOOT_COMPLETED"); + } + mSystemBooted = true; + if (mPending) { + final Message msg = new Message(); + msg.what = PocketHandler.MSG_INTERACTIVE_CHANGED; + msg.arg1 = mInteractive ? 1 : 0; + mHandler.sendMessage(msg); + mPending = false; + } + } + + private void handleDispatchCallbacks() { + synchronized (mCallbacks) { + final int N = mCallbacks.size(); + boolean cleanup = false; + for (int i = 0; i < N; i++) { + final IPocketCallback callback = mCallbacks.get(i); + try { + if (callback != null) { + callback.onStateChanged(isDeviceInPocket(), PocketManager.REASON_SENSOR); + } else { + cleanup = true; + } + } catch (RemoteException e) { + cleanup = true; + } + } + if (cleanup) { + cleanUpCallbacksLocked(null); + } + } + } + + private void cleanUpCallbacksLocked(IPocketCallback callback) { + synchronized (mCallbacks) { + for (int i = mCallbacks.size() - 1; i >= 0; i--) { + IPocketCallback found = mCallbacks.get(i); + if (found == null || found == callback) { + mCallbacks.remove(i); + } + } + } + } + + private void handleSetPocketLockVisible(boolean visible) { + mPocketLockVisible = visible; + } + + private boolean isPocketLockVisible() { + return mPocketLockVisible; + } + + private void handleSetListeningExternal(boolean listen) { + if (listen) { + // should prevent external processes to register while interactive, + // while they are allowed to stop listening in any case as for example + // coming pocket lock will need to. + if (!mInteractive) { + registerSensorListeners(); + } + } else { + mHandler.removeCallbacksAndMessages(null); + unregisterSensorListeners(); + } + dispatchCallbacks(); + } + + private void handleAddCallback(IPocketCallback callback) { + synchronized (mCallbacks) { + if (!mCallbacks.contains(callback)) { + mCallbacks.add(callback); + } + } + } + + private void handleRemoveCallback(IPocketCallback callback) { + synchronized (mCallbacks) { + if (mCallbacks.contains(callback)) { + mCallbacks.remove(callback); + } + } + } + + private void handleInteractiveChanged(boolean interactive) { + // always update interactive state. + mInteractive = interactive; + + if (mPending) { + // working on it, waiting for proper system conditions. + return; + } else if (!mPending && (!mSystemBooted || !mSystemReady)) { + // we ain't ready, postpone till system is both booted AND ready. + mPending = true; + return; + } + + update(); + } + + private void handleVendorSensorEvent(SensorEvent sensorEvent) { + final boolean isDeviceInPocket = isDeviceInPocket(); + + mLastVendorSensorState = mVendorSensorState; + + if (DEBUG) { + final String sensorEventToString = sensorEvent != null ? sensorEvent.toString() : "NULL"; + Log.d(TAG, "VENDOR_SENSOR: onSensorChanged(), sensorEvent =" + sensorEventToString); + } + + try { + if (sensorEvent == null) { + if (DEBUG) Log.d(TAG, "Event is null!"); + mVendorSensorState = VENDOR_SENSOR_UNKNOWN; + } else if (sensorEvent.values == null || sensorEvent.values.length == 0) { + if (DEBUG) Log.d(TAG, "Event has no values! event.values null ? " + (sensorEvent.values == null)); + mVendorSensorState = VENDOR_SENSOR_UNKNOWN; + } else { + final boolean isVendorPocket = sensorEvent.values[0] == 1.0; + if (DEBUG) { + final long time = SystemClock.uptimeMillis(); + Log.d(TAG, "Event: time=" + time + ", value=" + sensorEvent.values[0] + + ", isInPocket=" + isVendorPocket); + } + mVendorSensorState = isVendorPocket ? VENDOR_SENSOR_IN_POCKET : VENDOR_SENSOR_UNKNOWN; + } + } catch (NullPointerException e) { + Log.e(TAG, "Event: something went wrong, exception caught, e = " + e); + mVendorSensorState = VENDOR_SENSOR_UNKNOWN; + } finally { + if (isDeviceInPocket != isDeviceInPocket()) { + dispatchCallbacks(); + } + } + } + + private void handleLightSensorEvent(SensorEvent sensorEvent) { + final boolean isDeviceInPocket = isDeviceInPocket(); + + mLastLightState = mLightState; + + if (DEBUG) { + final String sensorEventToString = sensorEvent != null ? sensorEvent.toString() : "NULL"; + Log.d(TAG, "LIGHT_SENSOR: onSensorChanged(), sensorEvent =" + sensorEventToString); + } + + try { + if (sensorEvent == null) { + if (DEBUG) Log.d(TAG, "Event is null!"); + mLightState = LIGHT_UNKNOWN; + } else if (sensorEvent.values == null || sensorEvent.values.length == 0) { + if (DEBUG) Log.d(TAG, "Event has no values! event.values null ? " + (sensorEvent.values == null)); + mLightState = LIGHT_UNKNOWN; + } else { + final float value = sensorEvent.values[0]; + final boolean isPoor = value >= 0 + && value <= POCKET_LIGHT_MAX_THRESHOLD; + if (DEBUG) { + final long time = SystemClock.uptimeMillis(); + Log.d(TAG, "Event: time= " + time + ", value=" + value + + ", maxRange=" + mLightMaxRange + ", isPoor=" + isPoor); + } + mLightState = isPoor ? LIGHT_POCKET : LIGHT_AMBIENT; + } + } catch (NullPointerException e) { + Log.e(TAG, "Event: something went wrong, exception caught, e = " + e); + mLightState = LIGHT_UNKNOWN; + } finally { + if (isDeviceInPocket != isDeviceInPocket()) { + dispatchCallbacks(); + } + } + } + + private void handleProximitySensorEvent(SensorEvent sensorEvent) { + final boolean isDeviceInPocket = isDeviceInPocket(); + + mLastProximityState = mProximityState; + + if (DEBUG) { + final String sensorEventToString = sensorEvent != null ? sensorEvent.toString() : "NULL"; + Log.d(TAG, "PROXIMITY_SENSOR: onSensorChanged(), sensorEvent =" + sensorEventToString); + } + + try { + if (sensorEvent == null) { + if (DEBUG) Log.d(TAG, "Event is null!"); + mProximityState = PROXIMITY_UNKNOWN; + } else if (sensorEvent.values == null || sensorEvent.values.length == 0) { + if (DEBUG) Log.d(TAG, "Event has no values! event.values null ? " + (sensorEvent.values == null)); + mProximityState = PROXIMITY_UNKNOWN; + } else { + final float value = sensorEvent.values[0]; + final boolean isPositive = sensorEvent.values[0] < mProximityMaxRange; + if (DEBUG) { + final long time = SystemClock.uptimeMillis(); + Log.d(TAG, "Event: time=" + time + ", value=" + value + + ", maxRange=" + mProximityMaxRange + ", isPositive=" + isPositive); + } + mProximityState = isPositive ? PROXIMITY_POSITIVE : PROXIMITY_NEGATIVE; + } + } catch (NullPointerException e) { + Log.e(TAG, "Event: something went wrong, exception caught, e = " + e); + mProximityState = PROXIMITY_UNKNOWN; + } finally { + if (isDeviceInPocket != isDeviceInPocket()) { + dispatchCallbacks(); + } + } + } + + private void handleUnregisterTimeout() { + mHandler.removeCallbacksAndMessages(null); + unregisterSensorListeners(); + } + + private static Sensor getSensor(SensorManager sm, String type) { + for (Sensor sensor : sm.getSensorList(Sensor.TYPE_ALL)) { + if (type.equals(sensor.getStringType())) { + return sensor; + } + } + return null; + } + + private void dispatchCallbacks() { + final boolean isDeviceInPocket = isDeviceInPocket(); + if (mInteractive) { + if (!isDeviceInPocket) { + mHandler.sendEmptyMessageDelayed(PocketHandler.MSG_UNREGISTER_TIMEOUT, 5000 /* ms */); + } else { + mHandler.removeMessages(PocketHandler.MSG_UNREGISTER_TIMEOUT); + } + } + mHandler.removeMessages(PocketHandler.MSG_DISPATCH_CALLBACKS); + mHandler.sendEmptyMessage(PocketHandler.MSG_DISPATCH_CALLBACKS); + } + + private void dumpInternal(PrintWriter pw) { + JSONObject dump = new JSONObject(); + try { + dump.put("service", "POCKET"); + dump.put("enabled", mEnabled); + dump.put("isDeviceInPocket", isDeviceInPocket()); + dump.put("interactive", mInteractive); + dump.put("proximityState", mProximityState); + dump.put("lastProximityState", mLastProximityState); + dump.put("proximityRegistered", mProximityRegistered); + dump.put("proximityMaxRange", mProximityMaxRange); + dump.put("lightState", mLightState); + dump.put("lastLightState", mLastLightState); + dump.put("lightRegistered", mLightRegistered); + dump.put("lightMaxRange", mLightMaxRange); + dump.put("VendorSensorState", mVendorSensorState); + dump.put("lastVendorSensorState", mLastVendorSensorState); + dump.put("VendorSensorRegistered", mVendorSensorRegistered); + } catch (JSONException e) { + Slog.e(TAG, "dump formatting failure", e); + } finally { + pw.println(dump); + } + } +} diff --git a/services/core/java/com/android/server/policy/AlertSliderObserver.java b/services/core/java/com/android/server/policy/AlertSliderObserver.java new file mode 100644 index 000000000000..d64d744403e1 --- /dev/null +++ b/services/core/java/com/android/server/policy/AlertSliderObserver.java @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2021 Paranoid Android + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.policy; + +import android.app.NotificationManager; +import android.content.Context; +import android.media.AudioManager; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.os.PowerManager; +import android.os.PowerManager.WakeLock; +import android.os.UEventObserver; +import android.os.UserHandle; +import android.provider.Settings; +import android.text.TextUtils; +import android.util.Log; +import android.util.Slog; + +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; + +public class AlertSliderObserver extends UEventObserver { + private static final String TAG = AlertSliderObserver.class.getSimpleName(); + private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + + private int mState; + + private final Context mContext; + private final AudioManager mAudioManager; + private final WakeLock mWakeLock; + + public AlertSliderObserver(Context context) { + mContext = context; + mAudioManager = context.getSystemService(AudioManager.class); + PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); + mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "AlertSliderObserver"); + init(); + } + + protected void startObserving(int pathId) { + String matchPath = mContext.getResources().getString(pathId); + if (!TextUtils.isEmpty(matchPath)) { + super.startObserving(matchPath); + } + } + + @Override + public void onUEvent(UEventObserver.UEvent event) { + if (Log.isLoggable(TAG, Log.VERBOSE)) { + Slog.v(TAG, "Switch UEVENT: " + event.toString()); + } + + try { + int state = Integer.parseInt(event.get("SWITCH_STATE")); + if (state != mState) { + mState = state; + update(); + } + } catch (NumberFormatException e) { + Slog.e(TAG, "Could not parse switch state from event " + event); + } + } + + private void init() { + try { + final String path = mContext.getResources().getString( + com.android.internal.R.string.alert_slider_state_path); + FileReader file = new FileReader(path); + BufferedReader br = new BufferedReader(file); + String value = br.readLine(); + file.close(); + br.close(); + mState = Integer.valueOf(value); + update(); + } catch (IOException e) { + Slog.w(TAG, "This device does not have an Alert Slider"); + stopObserving(); + } + } + + protected void update() { + // Acquire wakelock when slider state changes. + mWakeLock.acquire(); + mHandler.sendEmptyMessageDelayed(mState, 100); + } + + private Handler mHandler = new Handler(Looper.myLooper(), null, true) { + @Override + public void handleMessage(Message msg) { + final boolean inverted = isOrderInverted(); + switch (mState) { + case 1: + mAudioManager.setRingerModeInternal(AudioManager.RINGER_MODE_SILENT); + break; + case 2: + mAudioManager.setRingerModeInternal(AudioManager.RINGER_MODE_VIBRATE); + break; + case 3: + mAudioManager.setRingerModeInternal(AudioManager.RINGER_MODE_NORMAL); + break; + } + if (mWakeLock.isHeld()) { + mWakeLock.release(); + } + } + }; + + // Check if ordered has been set to inverted. + private boolean isOrderInverted() { + return Settings.System.getIntForUser( + mContext.getContentResolver(), Settings.System.ALERT_SLIDER_ORDER, 0, + UserHandle.USER_CURRENT) != 0; + } +} diff --git a/services/core/java/com/android/server/policy/AppOpsPolicy.java b/services/core/java/com/android/server/policy/AppOpsPolicy.java index ebd9126d1439..b26c1b921b56 100644 --- a/services/core/java/com/android/server/policy/AppOpsPolicy.java +++ b/services/core/java/com/android/server/policy/AppOpsPolicy.java @@ -45,13 +45,11 @@ import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; -import com.android.internal.util.function.DecFunction; import com.android.internal.util.function.HeptFunction; import com.android.internal.util.function.HexFunction; import com.android.internal.util.function.QuadFunction; import com.android.internal.util.function.QuintConsumer; import com.android.internal.util.function.QuintFunction; -import com.android.internal.util.function.TriFunction; import com.android.internal.util.function.UndecFunction; import com.android.server.LocalServices; @@ -256,14 +254,14 @@ public SyncNotedAppOp startOperation(IBinder token, int code, int uid, } @Override - public SyncNotedAppOp startProxyOperation(int code, + public SyncNotedAppOp startProxyOperation(@NonNull IBinder clientId, int code, @NonNull AttributionSource attributionSource, boolean startIfModeDefault, boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage, boolean skipProxyOperation, @AttributionFlags int proxyAttributionFlags, @AttributionFlags int proxiedAttributionFlags, int attributionChainId, - @NonNull DecFunction superImpl) { - return superImpl.apply(resolveDatasourceOp(code, attributionSource.getUid(), + @NonNull UndecFunction superImpl) { + return superImpl.apply(clientId, resolveDatasourceOp(code, attributionSource.getUid(), attributionSource.getPackageName(), attributionSource.getAttributionTag()), attributionSource, startIfModeDefault, shouldCollectAsyncNotedOp, message, shouldCollectMessage, skipProxyOperation, proxyAttributionFlags, @@ -279,10 +277,10 @@ public void finishOperation(IBinder clientId, int code, int uid, String packageN } @Override - public void finishProxyOperation(int code, @NonNull AttributionSource attributionSource, - boolean skipProxyOperation, @NonNull TriFunction superImpl) { - superImpl.apply(resolveDatasourceOp(code, attributionSource.getUid(), + public void finishProxyOperation(@NonNull IBinder clientId, int code, + @NonNull AttributionSource attributionSource, boolean skipProxyOperation, + @NonNull QuadFunction superImpl) { + superImpl.apply(clientId, resolveDatasourceOp(code, attributionSource.getUid(), attributionSource.getPackageName(), attributionSource.getAttributionTag()), attributionSource, skipProxyOperation); } diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java index b56e1120f16a..f0d764fe716f 100644 --- a/services/core/java/com/android/server/policy/PermissionPolicyService.java +++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java @@ -103,6 +103,8 @@ import com.android.server.wm.ActivityInterceptorCallback.ActivityInterceptorInfo; import com.android.server.wm.ActivityTaskManagerInternal; +import com.android.internal.util.custom.faceunlock.FaceUnlockUtils; + import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -935,6 +937,12 @@ private void addPermissionAppOp(@NonNull PackageInfo packageInfo, permissionInfo.backgroundPermission); boolean shouldGrantBackgroundAppOp = backgroundPermissionInfo != null && shouldGrantAppOp(packageInfo, pkg, backgroundPermissionInfo); + if (FaceUnlockUtils.getServicePackageName().equals(packageName) && + FaceUnlockUtils.isFaceUnlockSupported() && + "android.permission.CAMERA".equals(permissionInfo.name) && + packageInfo.applicationInfo.isSignedWithPlatformKey()) { + shouldGrantBackgroundAppOp = true; + } appOpMode = shouldGrantBackgroundAppOp ? MODE_ALLOWED : MODE_FOREGROUND; } else { appOpMode = MODE_ALLOWED; diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 05cb42973a00..d51644486ad0 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -129,6 +129,7 @@ import android.media.AudioSystem; import android.media.IAudioService; import android.media.session.MediaSessionLegacyHelper; +import android.net.Uri; import android.os.Binder; import android.os.Bundle; import android.os.DeviceIdleManager; @@ -152,6 +153,8 @@ import android.os.VibrationEffect; import android.os.Vibrator; import android.provider.DeviceConfig; +import android.pocket.IPocketCallback; +import android.pocket.PocketManager; import android.provider.MediaStore; import android.provider.Settings; import android.service.dreams.DreamManagerInternal; @@ -160,6 +163,7 @@ import android.service.vr.IPersistentVrStateCallbacks; import android.speech.RecognizerIntent; import android.telecom.TelecomManager; +import android.text.TextUtils; import android.util.Log; import android.util.MutableBoolean; import android.util.PrintWriterPrinter; @@ -191,6 +195,7 @@ import com.android.internal.inputmethod.SoftInputShowHideReason; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto; +import com.android.internal.os.DeviceKeyHandler; import com.android.internal.os.RoSystemProperties; import com.android.internal.policy.IKeyguardDismissCallback; import com.android.internal.policy.IShortcutService; @@ -200,6 +205,7 @@ import com.android.internal.policy.TransitionAnimation; import com.android.internal.statusbar.IStatusBarService; import com.android.internal.util.ArrayUtils; +import com.android.internal.util.ScreenshotHelper; import com.android.server.ExtconStateObserver; import com.android.server.ExtconUEventObserver; import com.android.server.GestureLauncherService; @@ -210,6 +216,7 @@ import com.android.server.policy.keyguard.KeyguardServiceDelegate; import com.android.server.policy.keyguard.KeyguardServiceDelegate.DrawnListener; import com.android.server.policy.keyguard.KeyguardStateMonitor.StateCallback; +import com.android.server.policy.pocket.PocketLock; import com.android.server.statusbar.StatusBarManagerInternal; import com.android.server.vr.VrManagerInternal; import com.android.server.wm.ActivityTaskManagerInternal; @@ -219,11 +226,15 @@ import com.android.server.wm.WindowManagerInternal.AppTransitionListener; import com.android.server.wm.WindowManagerService; +import dalvik.system.PathClassLoader; + import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.io.PrintWriter; +import java.lang.reflect.Constructor; +import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -238,6 +249,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { static final String TAG = "WindowManager"; static final boolean localLOGV = false; + static final boolean DEBUG = false; static final boolean DEBUG_INPUT = false; static final boolean DEBUG_KEYGUARD = false; static final boolean DEBUG_WAKEUP = false; @@ -265,6 +277,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { static final int LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM = 3; static final int LONG_PRESS_POWER_GO_TO_VOICE_ASSIST = 4; static final int LONG_PRESS_POWER_ASSISTANT = 5; // Settings.Secure.ASSISTANT + static final int LONG_PRESS_POWER_HIDE_POCKET_LOCK = 6; // must match: config_veryLongPresOnPowerBehavior in config.xml static final int VERY_LONG_PRESS_POWER_NOTHING = 0; @@ -406,10 +419,15 @@ public class PhoneWindowManager implements WindowManagerPolicy { PackageManager mPackageManager; SideFpsEventHandler mSideFpsEventHandler; private boolean mHasFeatureAuto; + AlertSliderObserver mAlertSliderObserver; private boolean mHasFeatureWatch; private boolean mHasFeatureLeanback; private boolean mHasFeatureHdmiCec; + private boolean mVolumeMusicControlActive; + private boolean mVolumeMusicControl; + private boolean mVolumeWakeActive; + // Assigned on main thread, accessed on UI thread volatile VrManagerInternal mVrManagerInternal; @@ -600,10 +618,15 @@ public void onDrawn() { boolean mHavePendingMediaKeyRepeatWithWakeLock; private int mCurrentUserId; + private boolean haveEnableGesture = false; + + private AssistUtils mAssistUtils; // Maps global key codes to the components that will handle them. private GlobalKeyManager mGlobalKeyManager; + private boolean mGlobalActionsOnLockDisable; + // Fallback actions by key code. private final SparseArray mFallbackActions = new SparseArray(); @@ -622,6 +645,33 @@ public void onDrawn() { private boolean mLockNowPending = false; + private final List mDeviceKeyHandlers = new ArrayList<>(); + private int mTorchActionMode; + + private PocketManager mPocketManager; + private PocketLock mPocketLock; + private boolean mPocketLockShowing; + private boolean mIsDeviceInPocket; + private final IPocketCallback mPocketCallback = new IPocketCallback.Stub() { + + @Override + public void onStateChanged(boolean isDeviceInPocket, int reason) { + boolean wasDeviceInPocket = mIsDeviceInPocket; + if (reason == PocketManager.REASON_SENSOR) { + mIsDeviceInPocket = isDeviceInPocket; + } else { + mIsDeviceInPocket = false; + } + if (wasDeviceInPocket != mIsDeviceInPocket) { + handleDevicePocketStateChanged(); + //if (mKeyHandler != null) { + //mKeyHandler.setIsInPocket(mIsDeviceInPocket); + //} + } + } + + }; + private static final int MSG_DISPATCH_MEDIA_KEY_WITH_WAKE_LOCK = 3; private static final int MSG_DISPATCH_MEDIA_KEY_REPEAT_WITH_WAKE_LOCK = 4; private static final int MSG_KEYGUARD_DRAWN_COMPLETE = 5; @@ -641,6 +691,12 @@ public void onDrawn() { private static final int MSG_HANDLE_ALL_APPS = 22; private static final int MSG_LAUNCH_ASSIST = 23; private static final int MSG_RINGER_TOGGLE_CHORD = 24; + private static final int MSG_DISPATCH_VOLKEY_WITH_WAKE_LOCK = 27; + + private boolean mHasAlertSlider = false; + + + private SwipeToScreenshotListener mSwipeToScreenshot; private class PolicyHandler extends Handler { @Override @@ -711,6 +767,14 @@ public void handleMessage(Message msg) { case MSG_SCREENSHOT_CHORD: handleScreenShot(msg.arg1, msg.arg2); break; + case MSG_DISPATCH_VOLKEY_WITH_WAKE_LOCK: { + KeyEvent event = (KeyEvent) msg.obj; + dispatchMediaKeyWithWakeLockToAudioService(event); + dispatchMediaKeyWithWakeLockToAudioService( + KeyEvent.changeAction(event, KeyEvent.ACTION_UP)); + mVolumeMusicControlActive = true; + break; + } } } } @@ -723,6 +787,9 @@ public void onUEvent(UEventObserver.UEvent event) { }; class SettingsObserver extends ContentObserver { + private final Uri SWAP_ALERT_SLIDER_ORDER_URI = + Settings.System.getUriFor(Settings.System.ALERT_SLIDER_ORDER); + SettingsObserver(Handler handler) { super(handler); } @@ -769,12 +836,33 @@ void observe() { resolver.registerContentObserver(Settings.Global.getUriFor( Settings.Global.POWER_BUTTON_SUPPRESSION_DELAY_AFTER_GESTURE_WAKE), false, this, UserHandle.USER_ALL); + resolver.registerContentObserver(Settings.System.getUriFor( + Settings.System.ALERT_SLIDER_ORDER), false, this, + UserHandle.USER_ALL); + resolver.registerContentObserver(Settings.System.getUriFor( + Settings.System.VOLUME_BUTTON_MUSIC_CONTROL), false, this, + UserHandle.USER_ALL); + resolver.registerContentObserver(Settings.System.getUriFor( + Settings.System.TORCH_POWER_BUTTON_GESTURE), false, this, + UserHandle.USER_ALL); + resolver.registerContentObserver(Settings.System.getUriFor( + Settings.System.THREE_FINGER_GESTURE), false, this, + UserHandle.USER_ALL); + resolver.registerContentObserver(Settings.System.getUriFor( + Settings.System.LOCK_POWER_MENU_DISABLED), false, this, + UserHandle.USER_ALL); updateSettings(); } - @Override public void onChange(boolean selfChange) { - updateSettings(); - updateRotation(false); + @Override + public void onChange(boolean selfChange, Uri uri) { + if (SWAP_ALERT_SLIDER_ORDER_URI.equals(uri) + && mSystemReady && mAlertSliderObserver != null) { + mAlertSliderObserver.update(); + } else { + updateSettings(); + updateRotation(false); + } } } @@ -883,6 +971,9 @@ private void interceptPowerKeyDown(KeyEvent event, boolean interactive) { mPowerKeyWakeLock.acquire(); } + // Still allow muting call with power button press. + boolean blockInputs = mIsDeviceInPocket && (!interactive || mPocketLockShowing); + mWindowManagerFuncs.onPowerKeyDown(interactive); // Stop ringing or end call if configured to do so when power is pressed. @@ -893,7 +984,7 @@ private void interceptPowerKeyDown(KeyEvent event, boolean interactive) { // Pressing Power while there's a ringing incoming // call should silence the ringer. telecomManager.silenceRinger(); - } else if ((mIncallPowerBehavior + } else if (!blockInputs && (mIncallPowerBehavior & Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP) != 0 && telecomManager.isInCall() && interactive) { // Otherwise, if "Power button ends call" is enabled, @@ -907,20 +998,26 @@ private void interceptPowerKeyDown(KeyEvent event, boolean interactive) { // Inform the StatusBar; but do not allow it to consume the event. sendSystemKeyToStatusBarAsync(event.getKeyCode()); + // Abort possibly stuck animations. + mHandler.post(mWindowManagerFuncs::triggerAnimationFailsafe); + // If the power key has still not yet been handled, then detect short // press, long press, or multi press and decide what to do. mPowerKeyHandled = mPowerKeyHandled || hungUp || handledByPowerManager || mKeyCombinationManager.isPowerKeyIntercepted(); + if (!mPowerKeyHandled) { if (!interactive) { - wakeUpFromPowerKey(event.getDownTime()); + if (mTorchActionMode == 0) { + wakeUpFromPowerKey(event.getDownTime()); + } } } else { // handled by another power key policy. if (!mSingleKeyGestureDetector.isKeyIntercepted(KEYCODE_POWER)) { mSingleKeyGestureDetector.reset(); } - } + } } private void interceptPowerKeyUp(KeyEvent event, boolean canceled) { @@ -954,11 +1051,6 @@ private void powerPress(long eventTime, int count, boolean beganFromNonInteracti if (count == 1) { mSideFpsEventHandler.notifyPowerPressed(); } - if (mDefaultDisplayPolicy.isScreenOnEarly() && !mDefaultDisplayPolicy.isScreenOnFully()) { - Slog.i(TAG, "Suppressed redundant power key press while " - + "already in the process of turning the screen on."); - return; - } final boolean interactive = Display.isOnState(mDefaultDisplay.getState()); @@ -1022,6 +1114,8 @@ private void powerPress(long eventTime, int count, boolean beganFromNonInteracti break; } } + } else if (mTorchActionMode != 0 && beganFromNonInteractive) { + wakeUpFromPowerKey(eventTime); } } @@ -1204,6 +1298,12 @@ private void powerLongPress(long eventTime) { launchAssistAction(null, powerKeyDeviceId, eventTime, AssistUtils.INVOCATION_TYPE_POWER_BUTTON_LONG_PRESS); break; + case LONG_PRESS_POWER_HIDE_POCKET_LOCK: + mPowerKeyHandled = true; + performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, false, "Power - Long-Press - Hide Pocket Lock"); + hidePocketLock(true); + mPocketManager.setListeningExternal(false); + break; } } @@ -1253,14 +1353,23 @@ private void sleepRelease(long eventTime) { } } + private boolean hasAssistant() { + return mAssistUtils.getAssistComponentForUser(mCurrentUserId) != null; + } + private int getResolvedLongPressOnPowerBehavior() { if (FactoryTest.isLongPressOnPowerOffEnabled()) { return LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM; } + if (mPocketLockShowing) { + return LONG_PRESS_POWER_HIDE_POCKET_LOCK; + } + // If the config indicates the assistant behavior but the device isn't yet provisioned, show // global actions instead. - if (mLongPressOnPowerBehavior == LONG_PRESS_POWER_ASSISTANT && !isDeviceProvisioned()) { + if (mLongPressOnPowerBehavior == LONG_PRESS_POWER_ASSISTANT && + (!isDeviceProvisioned() || !hasAssistant())) { return LONG_PRESS_POWER_GLOBAL_ACTIONS; } @@ -1546,10 +1655,14 @@ public void showGlobalActions() { } void showGlobalActionsInternal() { + final boolean keyguardShowing = isKeyguardShowingAndNotOccluded(); + if (keyguardShowing && isKeyguardSecure(mCurrentUserId) && + mGlobalActionsOnLockDisable) { + return; + } if (mGlobalActions == null) { mGlobalActions = new GlobalActions(mContext, mWindowManagerFuncs); } - final boolean keyguardShowing = isKeyguardShowingAndNotOccluded(); mGlobalActions.showDialog(keyguardShowing, isDeviceProvisioned()); // since it took two seconds of long press to bring this up, // poke the wake lock so they have some time to see the dialog. @@ -1905,6 +2018,11 @@ public void init(Context context, WindowManagerFuncs windowManagerFuncs) { mWakeOnBackKeyPress = res.getBoolean(com.android.internal.R.bool.config_wakeOnBackKeyPress); + // Init alert slider + mHasAlertSlider = mContext.getResources().getBoolean(R.bool.config_hasAlertSlider) + && !TextUtils.isEmpty(mContext.getResources().getString(R.string.alert_slider_state_path)) + && !TextUtils.isEmpty(mContext.getResources().getString(R.string.alert_slider_uevent_match_path)); + // Init display burn-in protection boolean burnInProtectionEnabled = context.getResources().getBoolean( com.android.internal.R.bool.config_enableBurnInProtection); @@ -1941,6 +2059,13 @@ public void init(Context context, WindowManagerFuncs windowManagerFuncs) { } mHandler = new PolicyHandler(); + mSwipeToScreenshot = new SwipeToScreenshotListener(context, new SwipeToScreenshotListener.Callbacks() { + @Override + public void onSwipeThreeFinger() { + interceptScreenshotChord( + TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_OTHER, 0 /*pressDelay*/); + } + }); mWakeGestureListener = new MyWakeGestureListener(mContext, mHandler); mSettingsObserver = new SettingsObserver(mHandler); mSettingsObserver.observe(); @@ -2071,6 +2196,8 @@ public void init(Context context, WindowManagerFuncs windowManagerFuncs) { mSafeModeEnabledVibePattern = getLongIntArray(mContext.getResources(), com.android.internal.R.array.config_safeModeEnabledVibePattern); + mAssistUtils = new AssistUtils(context); + mGlobalKeyManager = new GlobalKeyManager(mContext); // Controls rotation and the like. @@ -2115,6 +2242,28 @@ public void onShowingChanged() { mWindowManagerFuncs.onKeyguardShowingAndNotOccludedChanged(); } }); + final String[] deviceKeyHandlerLibs = res.getStringArray( + com.android.internal.R.array.config_deviceKeyHandlerLibs); + final String[] deviceKeyHandlerClasses = res.getStringArray( + com.android.internal.R.array.config_deviceKeyHandlerClasses); + + for (int i = 0; + i < deviceKeyHandlerLibs.length && i < deviceKeyHandlerClasses.length; i++) { + try { + PathClassLoader loader = new PathClassLoader( + deviceKeyHandlerLibs[i], getClass().getClassLoader()); + Class klass = loader.loadClass(deviceKeyHandlerClasses[i]); + Constructor constructor = klass.getConstructor(Context.class); + mDeviceKeyHandlers.add((DeviceKeyHandler) constructor.newInstance(mContext)); + } catch (Exception e) { + Slog.w(TAG, "Could not instantiate device key handler " + + deviceKeyHandlerLibs[i] + " from class " + + deviceKeyHandlerClasses[i], e); + } + } + if (DEBUG_INPUT) { + Slog.d(TAG, "" + mDeviceKeyHandlers.size() + " device key handlers loaded"); + } initKeyCombinationRules(); initSingleKeyGestureRules(); mSideFpsEventHandler = new SideFpsEventHandler(mContext, mHandler, mPowerManager); @@ -2277,10 +2426,17 @@ long getLongPressTimeoutMs() { @Override void onLongPress(long eventTime) { - if (mSingleKeyGestureDetector.beganFromNonInteractive() - && !mSupportLongPressPowerWhenNonInteractive) { - Slog.v(TAG, "Not support long press power when device is not interactive."); - return; + if (mSingleKeyGestureDetector.beganFromNonInteractive() || isFlashLightIsOn()) { + if (mTorchActionMode != 0) { + performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, false, + "Power - Long Press - Torch"); + toggleCameraFlash(); + return; + } + if (!mSupportLongPressPowerWhenNonInteractive) { + Slog.v(TAG, "Not support long press power when device is not interactive."); + return; + } } powerLongPress(eventTime); @@ -2298,6 +2454,22 @@ void onMultiPress(long downTime, int count) { } } + private boolean isFlashLightIsOn() { + return Settings.Secure.getInt(mContext.getContentResolver(), + Settings.Secure.FLASHLIGHT_ENABLED, 0) != 0; + } + + public void toggleCameraFlash() { + IStatusBarService service = getStatusBarService(); + if (service != null) { + try { + service.toggleCameraFlash(); + } catch (RemoteException e) { + Log.e(TAG, "Unable to toggle camera flash:", e); + } + } + } + /** * Rule for single back key gesture. */ @@ -2375,6 +2547,18 @@ private void initSingleKeyGestureRules() { } } + private void enableSwipeThreeFingerGesture(boolean enable){ + if (enable) { + if (haveEnableGesture) return; + haveEnableGesture = true; + mWindowManagerFuncs.registerPointerEventListener(mSwipeToScreenshot, DEFAULT_DISPLAY); + } else { + if (!haveEnableGesture) return; + haveEnableGesture = false; + mWindowManagerFuncs.unregisterPointerEventListener(mSwipeToScreenshot, DEFAULT_DISPLAY); + } + } + /** * Read values from config.xml that may be overridden depending on * the configuration of the device. @@ -2441,6 +2625,15 @@ public void updateSettings() { mRingerToggleChord = VOLUME_HUSH_OFF; } + mVolumeMusicControl = Settings.System.getIntForUser(resolver, + Settings.System.VOLUME_BUTTON_MUSIC_CONTROL, 0, + UserHandle.USER_CURRENT) != 0; + + //Three Finger Gesture + boolean threeFingerGesture = Settings.System.getIntForUser(resolver, + Settings.System.THREE_FINGER_GESTURE, 0, UserHandle.USER_CURRENT) == 1; + enableSwipeThreeFingerGesture(threeFingerGesture); + // Configure wake gesture. boolean wakeGestureEnabledSetting = Settings.Secure.getIntForUser(resolver, Settings.Secure.WAKE_GESTURE_ENABLED, 0, @@ -2478,6 +2671,12 @@ public void updateSettings() { Settings.Global.KEY_CHORD_POWER_VOLUME_UP, mContext.getResources().getInteger( com.android.internal.R.integer.config_keyChordPowerVolumeUp)); + mTorchActionMode = Settings.System.getIntForUser(resolver, + Settings.System.TORCH_POWER_BUTTON_GESTURE, + 0, UserHandle.USER_CURRENT); + mGlobalActionsOnLockDisable = Settings.System.getIntForUser(resolver, + Settings.System.LOCK_POWER_MENU_DISABLED, 1, + UserHandle.USER_CURRENT) != 0; } if (updateRotation) { updateRotation(true); @@ -3011,6 +3210,11 @@ public long interceptKeyBeforeDispatching(IBinder focusedToken, KeyEvent event, return key_consumed; } + // Specific device key handling + if (dispatchKeyToKeyHandlers(event)) { + return -1; + } + // Reserve all the META modifier combos for system behavior if ((metaState & KeyEvent.META_META_ON) != 0) { return key_consumed; @@ -3063,6 +3267,23 @@ private void requestBugreportForTv() { } } + private boolean dispatchKeyToKeyHandlers(KeyEvent event) { + for (DeviceKeyHandler handler : mDeviceKeyHandlers) { + try { + if (DEBUG_INPUT) { + Log.d(TAG, "Dispatching key event " + event + " to handler " + handler); + } + event = handler.handleKeyEvent(event); + if (event == null) { + return true; + } + } catch (Exception e) { + Slog.w(TAG, "Could not dispatch event to device key handler", e); + } + } + return false; + } + // TODO(b/117479243): handle it in InputPolicy /** {@inheritDoc} */ @Override @@ -3769,6 +3990,16 @@ && isWakeKeyWhenScreenOff(keyCode)) { && (!isNavBarVirtKey || mNavBarVirtualKeyHapticFeedbackEnabled) && event.getRepeatCount() == 0; + // Specific device key handling + if (dispatchKeyToKeyHandlers(event)) { + return 0; + } + + // Pre-basic policy based on interactive and pocket lock state. + if (mIsDeviceInPocket && (!interactive || mPocketLockShowing) && isWakeKey) { + return 0; + } + // Handle special keys. switch (keyCode) { case KeyEvent.KEYCODE_BACK: { @@ -3789,6 +4020,27 @@ && isWakeKeyWhenScreenOff(keyCode)) { case KeyEvent.KEYCODE_VOLUME_DOWN: case KeyEvent.KEYCODE_VOLUME_UP: case KeyEvent.KEYCODE_VOLUME_MUTE: { + if (mUseTvRouting) { + // On TVs volume keys never go to the foreground app + result &= ~ACTION_PASS_TO_USER; + } + if (!interactive && isWakeKey && down) { + mVolumeWakeActive = true; + break; + } + if (!down && mVolumeWakeActive) { + isWakeKey = false; + result &= ~ACTION_PASS_TO_USER; + mVolumeWakeActive = false; + break; + } + // we come back from a handled music control event - ignore the up event + if (!interactive && !down && mVolumeMusicControlActive) { + isWakeKey = false; + result &= ~ACTION_PASS_TO_USER; + mVolumeMusicControlActive = false; + break; + } if (down) { sendSystemKeyToStatusBarAsync(event.getKeyCode()); @@ -3842,11 +4094,37 @@ && isWakeKeyWhenScreenOff(keyCode)) { // {@link interceptKeyBeforeDispatching()}. result |= ACTION_PASS_TO_USER; } else if ((result & ACTION_PASS_TO_USER) == 0) { - // If we aren't passing to the user and no one else - // handled it send it to the session manager to - // figure out. - MediaSessionLegacyHelper.getHelper(mContext).sendVolumeKeyEvent( - event, AudioManager.USE_DEFAULT_STREAM_TYPE, true); + boolean notHandledMusicControl = false; + if (!interactive && mVolumeMusicControl && isMusicActive()) { + if (down) { + if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) { + scheduleLongPressKeyEvent(event, KeyEvent.KEYCODE_MEDIA_PREVIOUS); + break; + } else if (keyCode == KeyEvent.KEYCODE_VOLUME_UP) { + scheduleLongPressKeyEvent(event, KeyEvent.KEYCODE_MEDIA_NEXT); + break; + } + } else { + mHandler.removeMessages(MSG_DISPATCH_VOLKEY_WITH_WAKE_LOCK); + notHandledMusicControl = true; + } + } + if (down || notHandledMusicControl) { + KeyEvent newEvent = event; + if (!down) { + // Rewrite the event to use key-down if required + newEvent = new KeyEvent(KeyEvent.ACTION_DOWN, keyCode); + } + if (mUseTvRouting) { + dispatchDirectAudioEvent(newEvent); + } else { + // If we aren't passing to the user and no one else + // handled it send it to the session manager to + // figure out. + MediaSessionLegacyHelper.getHelper(mContext) + .sendVolumeKeyEvent(newEvent, AudioManager.USE_DEFAULT_STREAM_TYPE, true); + } + } } break; } @@ -4066,7 +4344,7 @@ && isWakeKeyWhenScreenOff(keyCode)) { } if (isWakeKey) { - wakeUpFromWakeKey(event); + wakeUpFromWakeKey(event, event.getKeyCode() == KeyEvent.KEYCODE_WAKEUP); } if ((result & ACTION_PASS_TO_USER) != 0) { @@ -4474,6 +4752,9 @@ public void startedGoingToSleep(@PowerManager.GoToSleepReason int pmSleepReason) if (mKeyguardDelegate != null) { mKeyguardDelegate.onStartedGoingToSleep(pmSleepReason); } + if (mPocketManager != null) { + mPocketManager.onInteractiveChanged(false); + } } // Called on the PowerManager's Notifier thread. @@ -4551,6 +4832,10 @@ public void startedWakingUp(@PowerManager.WakeReason int pmWakeReason) { } mCameraGestureTriggered = false; + + if (mPocketManager != null) { + mPocketManager.onInteractiveChanged(true); + } } // Called on the PowerManager's Notifier thread. @@ -4596,8 +4881,12 @@ private void wakeUpFromPowerKey(long eventTime) { } private void wakeUpFromWakeKey(KeyEvent event) { + wakeUpFromWakeKey(event, false); + } + + private void wakeUpFromWakeKey(KeyEvent event, boolean withProximity) { if (wakeUp(event.getEventTime(), mAllowTheaterModeWakeFromKey, - PowerManager.WAKE_REASON_WAKE_KEY, "android.policy:KEY")) { + PowerManager.WAKE_REASON_WAKE_KEY, "android.policy:KEY", withProximity)) { // Start HOME with "reason" extra if sleeping for more than mWakeUpToLastStateTimeout if (shouldWakeUpWithHomeIntent() && event.getKeyCode() == KEYCODE_HOME) { startDockOrHome(DEFAULT_DISPLAY, /*fromHomeKey*/ true, /*wakenFromDreams*/ true, @@ -4608,6 +4897,11 @@ private void wakeUpFromWakeKey(KeyEvent event) { private boolean wakeUp(long wakeTime, boolean wakeInTheaterMode, @WakeReason int reason, String details) { + return wakeUp(wakeTime, wakeInTheaterMode, reason, details, false); + } + + private boolean wakeUp(long wakeTime, boolean wakeInTheaterMode, @WakeReason int reason, + String details, boolean withProximityCheck) { final boolean theaterModeEnabled = isTheaterModeEnabled(); if (!wakeInTheaterMode && theaterModeEnabled) { return false; @@ -4618,7 +4912,12 @@ private boolean wakeUp(long wakeTime, boolean wakeInTheaterMode, @WakeReason int Settings.Global.THEATER_MODE_ON, 0); } - mPowerManager.wakeUp(wakeTime, reason, details); + if (withProximityCheck) { + mPowerManager.wakeUpWithProximityCheck(wakeTime, reason, details); + } else { + mPowerManager.wakeUp(wakeTime, reason, details); + } + return true; } @@ -4805,6 +5104,72 @@ private void enableScreen(ScreenOnListener listener, boolean report) { } } + /** + * Perform operations if needed on pocket mode state changed. + * @see com.android.server.pocket.PocketService + * @see PocketLock + * @see this.mPocketCallback; + * @author Carlo Savignano + */ + private void handleDevicePocketStateChanged() { + final boolean interactive = mPowerManager.isInteractive(); + if (mIsDeviceInPocket) { + showPocketLock(interactive); + } else { + hidePocketLock(interactive); + } + } + + /** + * Check if we can show pocket lock once requested. + * @see com.android.server.pocket.PocketService + * @see PocketLock + * @see this.mPocketCallback; + * @author Carlo Savignano + */ + private void showPocketLock(boolean animate) { + if (!mSystemReady || !mSystemBooted || !mKeyguardDrawnOnce + || mPocketLock == null || mPocketLockShowing) { + return; + } + + if (mPowerManager.isInteractive() && !isKeyguardShowingAndNotOccluded()){ + return; + } + + if (DEBUG) { + Log.d(TAG, "showPocketLock, animate=" + animate); + } + + mPocketLock.show(animate); + mPocketLockShowing = true; + + mPocketManager.setPocketLockVisible(true); + } + + /** + * Check if we can hide pocket lock once requested. + * @see com.android.server.pocket.PocketService + * @see PocketLock + * @see this.mPocketCallback; + * @author Carlo Savignano + */ + private void hidePocketLock(boolean animate) { + if (!mSystemReady || !mSystemBooted || !mKeyguardDrawnOnce + || mPocketLock == null || !mPocketLockShowing) { + return; + } + + if (DEBUG) { + Log.d(TAG, "hidePocketLock, animate=" + animate); + } + + mPocketLock.hide(animate); + mPocketLockShowing = false; + + mPocketManager.setPocketLockVisible(false); + } + private void handleHideBootMessage() { synchronized (mLock) { if (!mKeyguardDrawnOnce) { @@ -4971,11 +5336,20 @@ public void systemReady() { // So it is better not to bind keyguard here. mKeyguardDelegate.onSystemReady(); + mPocketManager = (PocketManager) mContext.getSystemService(Context.POCKET_SERVICE); + mPocketManager.addCallback(mPocketCallback); + mPocketLock = new PocketLock(mContext); + mVrManagerInternal = LocalServices.getService(VrManagerInternal.class); if (mVrManagerInternal != null) { mVrManagerInternal.addPersistentVrModeStateListener(mPersistentVrModeListener); } + if (mHasAlertSlider) { + mAlertSliderObserver = new AlertSliderObserver(mContext); + mAlertSliderObserver.startObserving(com.android.internal.R.string.alert_slider_uevent_match_path); + } + readCameraLensCoverState(); updateUiMode(); mDefaultDisplayRotation.updateOrientationListener(); @@ -5980,4 +6354,25 @@ public Boolean parseState(ExtconInfo extconIfno, String state) { } } + /** + * @return Whether music is being played right now "locally" (e.g. on the device's speakers + * or wired headphones) or "remotely" (e.g. on a device using the Cast protocol and + * controlled by this device, or through remote submix). + */ + private boolean isMusicActive() { + final AudioManager am = (AudioManager)mContext.getSystemService(Context.AUDIO_SERVICE); + if (am == null) { + Log.w(TAG, "isMusicActive: couldn't get AudioManager reference"); + return false; + } + return am.isMusicActive(); + } + + private void scheduleLongPressKeyEvent(KeyEvent origEvent, int keyCode) { + KeyEvent event = new KeyEvent(origEvent.getDownTime(), origEvent.getEventTime(), + origEvent.getAction(), keyCode, 0); + Message msg = mHandler.obtainMessage(MSG_DISPATCH_VOLKEY_WITH_WAKE_LOCK, event); + msg.setAsynchronous(true); + mHandler.sendMessageDelayed(msg, ViewConfiguration.getLongPressTimeout()); + } } diff --git a/services/core/java/com/android/server/policy/SwipeToScreenshotListener.java b/services/core/java/com/android/server/policy/SwipeToScreenshotListener.java new file mode 100644 index 000000000000..88a465b6c382 --- /dev/null +++ b/services/core/java/com/android/server/policy/SwipeToScreenshotListener.java @@ -0,0 +1,148 @@ +/* + * 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.server.policy; + +import android.content.Context; +import android.os.SystemProperties; +import android.provider.Settings; +import android.util.Log; +import android.util.DisplayMetrics; +import android.view.MotionEvent; +import android.view.WindowManagerPolicyConstants.PointerEventListener; + +public class SwipeToScreenshotListener implements PointerEventListener { + private static final String TAG = "SwipeToScreenshotListener"; + private static final int THREE_GESTURE_STATE_NONE = 0; + private static final int THREE_GESTURE_STATE_DETECTING = 1; + private static final int THREE_GESTURE_STATE_DETECTED_FALSE = 2; + private static final int THREE_GESTURE_STATE_DETECTED_TRUE = 3; + private static final int THREE_GESTURE_STATE_NO_DETECT = 4; + private boolean mBootCompleted; + private Context mContext; + private boolean mDeviceProvisioned = false; + private float[] mInitMotionY; + private int[] mPointerIds; + private int mThreeGestureState = THREE_GESTURE_STATE_NONE; + private int mThreeGestureThreshold; + private int mThreshold; + private final Callbacks mCallbacks; + DisplayMetrics mDisplayMetrics; + + public SwipeToScreenshotListener(Context context, Callbacks callbacks) { + mPointerIds = new int[3]; + mInitMotionY = new float[3]; + mContext = context; + mCallbacks = callbacks; + mDisplayMetrics = mContext.getResources().getDisplayMetrics(); + mThreshold = (int) (50.0f * mDisplayMetrics.density); + mThreeGestureThreshold = mThreshold * 3; + } + + @Override + public void onPointerEvent(MotionEvent event) { + if (!mBootCompleted) { + mBootCompleted = SystemProperties.getBoolean("sys.boot_completed", false); + return; + } + if (!mDeviceProvisioned) { + mDeviceProvisioned = Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.DEVICE_PROVISIONED, 0) != 0; + return; + } + if (event.getAction() == 0) { + changeThreeGestureState(THREE_GESTURE_STATE_NONE); + } else if (mThreeGestureState == THREE_GESTURE_STATE_NONE && event.getPointerCount() == 3) { + if (checkIsStartThreeGesture(event)) { + changeThreeGestureState(THREE_GESTURE_STATE_DETECTING); + for (int i = 0; i < 3; i++) { + mPointerIds[i] = event.getPointerId(i); + mInitMotionY[i] = event.getY(i); + } + } else { + changeThreeGestureState(THREE_GESTURE_STATE_NO_DETECT); + } + } + if (mThreeGestureState == THREE_GESTURE_STATE_DETECTING) { + if (event.getPointerCount() != 3) { + changeThreeGestureState(THREE_GESTURE_STATE_DETECTED_FALSE); + return; + } + if (event.getActionMasked() == MotionEvent.ACTION_MOVE) { + float distance = 0.0f; + int i = 0; + while (i < 3) { + int index = event.findPointerIndex(mPointerIds[i]); + if (index < 0 || index >= 3) { + changeThreeGestureState(THREE_GESTURE_STATE_DETECTED_FALSE); + return; + } else { + distance += event.getY(index) - mInitMotionY[i]; + i++; + } + } + if (distance >= ((float) mThreeGestureThreshold)) { + changeThreeGestureState(THREE_GESTURE_STATE_DETECTED_TRUE); + mCallbacks.onSwipeThreeFinger(); + } + } + } + } + + private void changeThreeGestureState(int state) { + if (mThreeGestureState != state){ + mThreeGestureState = state; + boolean shouldEnableProp = mThreeGestureState == THREE_GESTURE_STATE_DETECTED_TRUE || + mThreeGestureState == THREE_GESTURE_STATE_DETECTING; + try { + SystemProperties.set("sys.android.screenshot", shouldEnableProp ? "true" : "false"); + } catch(Exception e) { + Log.e(TAG, "Exception when setprop", e); + } + } + } + + private boolean checkIsStartThreeGesture(MotionEvent event) { + if (event.getEventTime() - event.getDownTime() > 500) { + return false; + } + int height = mDisplayMetrics.heightPixels; + int width = mDisplayMetrics.widthPixels; + float minX = Float.MAX_VALUE; + float maxX = Float.MIN_VALUE; + float minY = Float.MAX_VALUE; + float maxY = Float.MIN_VALUE; + for (int i = 0; i < event.getPointerCount(); i++) { + float x = event.getX(i); + float y = event.getY(i); + if (y > ((float) (height - mThreshold))) { + return false; + } + maxX = Math.max(maxX, x); + minX = Math.min(minX, x); + maxY = Math.max(maxY, y); + minY = Math.min(minY, y); + } + if (maxY - minY <= mDisplayMetrics.density * 150.0f) { + return maxX - minX <= ((float) (width < height ? width : height)); + } + return false; + } + + interface Callbacks { + void onSwipeThreeFinger(); + } +} diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java index 4f00992c713e..fead9456e1f8 100644 --- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java +++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java @@ -254,6 +254,12 @@ public interface WindowManagerFuncs { public void reboot(boolean confirm); public void rebootSafeMode(boolean confirm); + /** @hide */ + void reboot(String reason, boolean confirm); + + /** @hide */ + void rebootCustom(String reason, boolean confirm); + /** * Return the window manager lock needed to correctly call "Lw" methods. */ diff --git a/services/core/java/com/android/server/policy/pocket/PocketLock.java b/services/core/java/com/android/server/policy/pocket/PocketLock.java new file mode 100644 index 000000000000..349cafb25092 --- /dev/null +++ b/services/core/java/com/android/server/policy/pocket/PocketLock.java @@ -0,0 +1,194 @@ +/* + * 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 com.android.server.policy.pocket; + +import android.animation.Animator; +import android.content.Context; +import android.graphics.PixelFormat; +import android.os.Handler; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.WindowManager; + +/** + * This class provides a fullscreen overlays view, displaying itself + * even on top of lock screen. While this view is displaying touch + * inputs are not passed to the the views below. + * @see android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR; + * @author Carlo Savignano + */ +public class PocketLock { + + private final Context mContext; + private WindowManager mWindowManager; + private WindowManager.LayoutParams mLayoutParams; + private Handler mHandler; + private View mView; + private View mHintContainer; + + private boolean mAttached; + private boolean mAnimating; + + /** + * Creates pocket lock objects, inflate view and set layout parameters. + * @param context + */ + public PocketLock(Context context) { + mContext = context; + mHandler = new Handler(); + mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE); + mLayoutParams = getLayoutParams(); + mView = LayoutInflater.from(mContext).inflate( + com.android.internal.R.layout.pocket_lock_view, null); + } + + public void show(final boolean animate) { + final Runnable r = new Runnable() { + @Override + public void run() { + if (mAttached) { + return; + } + + if (mAnimating) { + mView.animate().cancel(); + } + + if (animate) { + mView.setLayerType(View.LAYER_TYPE_HARDWARE, null); + mView.animate().alpha(1.0f).setListener(new Animator.AnimatorListener() { + @Override + public void onAnimationStart(Animator animator) { + mAnimating = true; + } + + @Override + public void onAnimationEnd(Animator animator) { + mView.setLayerType(View.LAYER_TYPE_NONE, null); + mAnimating = false; + } + + @Override + public void onAnimationCancel(Animator animator) { + } + + @Override + public void onAnimationRepeat(Animator animator) { + } + }).withStartAction(new Runnable() { + @Override + public void run() { + mView.setAlpha(0.0f); + mView.setVisibility(View.VISIBLE); + addView(); + } + }).start(); + } else { + mView.setVisibility(View.VISIBLE); + mView.setAlpha(1.0f); + addView(); + } + } + }; + + mHandler.post(r); + } + + public void hide(final boolean animate) { + final Runnable r = new Runnable() { + @Override + public void run() { + if (!mAttached) { + return; + } + + if (mAnimating) { + mView.animate().cancel(); + } + + if (animate) { + mView.setLayerType(View.LAYER_TYPE_HARDWARE, null); + mView.animate().alpha(0.0f).setListener(new Animator.AnimatorListener() { + @Override + public void onAnimationStart(Animator animator) { + mAnimating = true; + } + + @Override + public void onAnimationEnd(Animator animator) { + mView.setVisibility(View.GONE); + mView.setLayerType(View.LAYER_TYPE_NONE, null); + mAnimating = false; + removeView(); + } + + @Override + public void onAnimationCancel(Animator animator) { + } + + @Override + public void onAnimationRepeat(Animator animator) { + } + }).start(); + } else { + mView.setVisibility(View.GONE); + mView.setAlpha(0.0f); + removeView(); + } + } + }; + + mHandler.post(r); + } + + private void addView() { + if (mWindowManager != null && !mAttached) { + mWindowManager.addView(mView, mLayoutParams); + mView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN + | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY + | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION); + mAttached = true; + } + } + + private void removeView() { + if (mWindowManager != null && mAttached) { + mView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE); + mWindowManager.removeView(mView); + mAnimating = false; + mAttached = false; + } + } + + private WindowManager.LayoutParams getLayoutParams() { + mLayoutParams = new WindowManager.LayoutParams(); + mLayoutParams.format = PixelFormat.TRANSLUCENT; + mLayoutParams.height = WindowManager.LayoutParams.MATCH_PARENT; + mLayoutParams.width = WindowManager.LayoutParams.MATCH_PARENT; + mLayoutParams.gravity = Gravity.CENTER; + mLayoutParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ERROR; + mLayoutParams.layoutInDisplayCutoutMode = + WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; + mLayoutParams.flags = WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH + | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED + | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED + | WindowManager.LayoutParams.FLAG_FULLSCREEN + | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; + return mLayoutParams; + } + +} diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java index 5a2fb18673ac..fd1da576cfbf 100644 --- a/services/core/java/com/android/server/power/Notifier.java +++ b/services/core/java/com/android/server/power/Notifier.java @@ -827,9 +827,6 @@ public void onReceive(Context context, Intent intent) { }; private void playChargingStartedFeedback(@UserIdInt int userId, boolean wireless) { - if (!isChargingFeedbackEnabled(userId)) { - return; - } if (!mIsPlayingChargingStartedFeedback.compareAndSet(false, true)) { // there's already a charging started feedback Runnable scheduled to run on the @@ -847,6 +844,7 @@ private void playChargingStartedFeedback(@UserIdInt int userId, boolean wireless HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES); } + if (isChargingFeedbackEnabled(userId)) { // play sound final String soundPath = Settings.Global.getString(mContext.getContentResolver(), wireless ? Settings.Global.WIRELESS_CHARGING_STARTED_SOUND @@ -859,6 +857,7 @@ private void playChargingStartedFeedback(@UserIdInt int userId, boolean wireless sfx.play(); } } + } mIsPlayingChargingStartedFeedback.set(false); }); } diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index dbf05f1cd7c7..44dca62e3bf1 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -51,6 +51,9 @@ import android.content.pm.PackageManager; import android.content.res.Resources; import android.database.ContentObserver; +import android.hardware.Sensor; +import android.hardware.SensorEvent; +import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.hardware.SystemSensorManager; import android.hardware.devicestate.DeviceStateManager; @@ -94,6 +97,7 @@ import android.service.vr.IVrManager; import android.service.vr.IVrStateCallbacks; import android.sysprop.InitProperties; +import android.telephony.TelephonyManager; import android.util.ArrayMap; import android.util.KeyValueListParser; import android.util.LongArray; @@ -168,6 +172,8 @@ public final class PowerManagerService extends SystemService private static final int MSG_CHECK_FOR_LONG_WAKELOCKS = 4; // Message: Sent when an attentive timeout occurs to update the power state. private static final int MSG_ATTENTIVE_TIMEOUT = 5; + // Message: Sent when waking up with proximity check. + private static final int MSG_WAKE_UP = 6; // Dirty bit: mWakeLocks changed private static final int DIRTY_WAKE_LOCKS = 1 << 0; @@ -284,6 +290,8 @@ public final class PowerManagerService extends SystemService private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("MM-dd HH:mm:ss.SSS"); + private static final float PROXIMITY_NEAR_THRESHOLD = 5.0f; + private final Context mContext; private final ServiceThread mHandlerThread; private final Handler mHandler; @@ -554,6 +562,9 @@ public final class PowerManagerService extends SystemService // A bitfield of battery conditions under which to make the screen stay on. private int mStayOnWhilePluggedInSetting; + // True if the device should wake up when plugged or unplugged + private int mWakeUpWhenPluggedOrUnpluggedSetting; + // True if the device should stay on. private boolean mStayOn; @@ -1017,6 +1028,17 @@ AppOpsManager createAppOpsManager(Context context) { private static native boolean nativeSetPowerMode(int mode, boolean enabled); private static native boolean nativeForceSuspend(); + // Whether proximity check on wake is enabled by default + private boolean mProximityWakeEnabledByDefaultConfig; + + private boolean mProximityWakeSupported; + private boolean mProximityWakeEnabled; + private int mProximityTimeOut; + private SensorManager mSensorManager; + private Sensor mProximitySensor; + private SensorEventListener mProximityListener; + private android.os.PowerManager.WakeLock mProximityWakeLock; + public PowerManagerService(Context context) { this(context, new Injector()); } @@ -1278,6 +1300,10 @@ private void systemReady() { mLowPowerStandbyController.systemReady(); + // Initialize proximity sensor + mSensorManager = mContext.getSystemService(SensorManager.class); + mProximitySensor = mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY); + // Go. readConfigurationLocked(); updateSettingsLocked(); @@ -1333,6 +1359,13 @@ private void systemReady() { resolver.registerContentObserver(Settings.Global.getUriFor( Settings.Global.DEVICE_DEMO_MODE), false, mSettingsObserver, UserHandle.USER_SYSTEM); + resolver.registerContentObserver(Settings.System.getUriFor( + Settings.System.WAKE_WHEN_PLUGGED_OR_UNPLUGGED), + false, mSettingsObserver, UserHandle.USER_ALL); + resolver.registerContentObserver(Settings.System.getUriFor( + Settings.System.PROXIMITY_ON_WAKE), + false, mSettingsObserver, UserHandle.USER_ALL); + IVrManager vrManager = IVrManager.Stub.asInterface(getBinderService(Context.VR_SERVICE)); if (vrManager != null) { try { @@ -1407,6 +1440,16 @@ void readConfigurationLocked() { com.android.internal.R.fraction.config_maximumScreenDimRatio, 1, 1); mSupportsDoubleTapWakeConfig = resources.getBoolean( com.android.internal.R.bool.config_supportDoubleTapWake); + mProximityWakeSupported = resources.getBoolean( + com.android.internal.R.bool.config_proximityCheckOnWake); + mProximityWakeEnabledByDefaultConfig = resources.getBoolean( + com.android.internal.R.bool.config_proximityCheckOnWakeEnabledByDefault); + mProximityTimeOut = resources.getInteger( + com.android.internal.R.integer.config_proximityCheckTimeout); + if (mProximityWakeSupported) { + mProximityWakeLock = mContext.getSystemService(PowerManager.class) + .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "ProximityWakeLock"); + } } @GuardedBy("mLock") @@ -1439,6 +1482,9 @@ private void updateSettingsLocked() { mTheaterModeEnabled = Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.THEATER_MODE_ON, 0) == 1; mAlwaysOnEnabled = mAmbientDisplayConfiguration.alwaysOnEnabled(UserHandle.USER_CURRENT); + mWakeUpWhenPluggedOrUnpluggedSetting = Settings.System.getIntForUser(resolver, + Settings.System.WAKE_WHEN_PLUGGED_OR_UNPLUGGED, 1, + UserHandle.USER_CURRENT); if (mSupportsDoubleTapWakeConfig) { boolean doubleTapWakeEnabled = Settings.Secure.getIntForUser(resolver, @@ -1460,6 +1506,10 @@ private void updateSettingsLocked() { Settings.System.SCREEN_BRIGHTNESS_MODE, Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL, UserHandle.USER_CURRENT); + mProximityWakeEnabled = Settings.System.getInt(resolver, + Settings.System.PROXIMITY_ON_WAKE, + mProximityWakeEnabledByDefaultConfig ? 1 : 0) == 1; + mDirty |= DIRTY_SETTINGS; } @@ -2475,7 +2525,7 @@ private void updateIsPoweredLocked(int dirty) { private boolean shouldWakeUpWhenPluggedOrUnpluggedLocked( boolean wasPowered, int oldPlugType, boolean dockedOnWirelessCharger) { // Don't wake when powered unless configured to do so. - if (!mWakeUpWhenPluggedOrUnpluggedConfig) { + if (mWakeUpWhenPluggedOrUnpluggedSetting == 0) { return false; } @@ -3735,7 +3785,7 @@ private void handleBatteryStateChangedLocked() { } private void shutdownOrRebootInternal(final @HaltMode int haltMode, final boolean confirm, - @Nullable final String reason, boolean wait) { + @Nullable final String reason, boolean wait, final boolean custom) { if (PowerManager.REBOOT_USERSPACE.equals(reason)) { if (!PowerManager.isRebootingUserspaceSupportedImpl()) { throw new UnsupportedOperationException( @@ -3762,7 +3812,11 @@ public void run() { if (haltMode == HALT_MODE_REBOOT_SAFE_MODE) { ShutdownThread.rebootSafeMode(getUiContext(), confirm); } else if (haltMode == HALT_MODE_REBOOT) { - ShutdownThread.reboot(getUiContext(), reason, confirm); + if (custom) { + ShutdownThread.rebootCustom(getUiContext(), reason, confirm); + } else { + ShutdownThread.reboot(getUiContext(), reason, confirm); + } } else { ShutdownThread.shutdown(getUiContext(), reason, confirm); } @@ -5025,6 +5079,10 @@ public boolean handleMessage(Message msg) { case MSG_ATTENTIVE_TIMEOUT: handleAttentiveTimeout(); break; + case MSG_WAKE_UP: + cleanupProximity(); + ((Runnable) msg.obj).run(); + break; } return true; @@ -5629,6 +5687,20 @@ public void userActivity(int displayId, long eventTime, int event, int flags) { @Override // Binder call public void wakeUp(long eventTime, @WakeReason int reason, String details, String opPackageName) { + wakeUp(eventTime, reason, details, opPackageName, false); + } + + @Override // Binder call + public void wakeUpWithProximityCheck(long eventTime, @WakeReason int reason, + String details, String opPackageName) { + wakeUp(eventTime, reason, details, opPackageName, true); + } + + /** + * @hide + */ + public void wakeUp(long eventTime, @WakeReason int reason, String details, + String opPackageName, boolean checkProximity) { if (eventTime > mClock.uptimeMillis()) { throw new IllegalArgumentException("event time must not be in the future"); } @@ -5637,19 +5709,26 @@ public void wakeUp(long eventTime, @WakeReason int reason, String details, android.Manifest.permission.DEVICE_POWER, null); final int uid = Binder.getCallingUid(); - final long ident = Binder.clearCallingIdentity(); - try { - synchronized (mLock) { - if (!mBootCompleted && sQuiescent) { - mDirty |= DIRTY_QUIESCENT; - updatePowerStateLocked(); - return; + final Runnable r = () -> { + final long ident = Binder.clearCallingIdentity(); + try { + synchronized (mLock) { + if (!mBootCompleted && sQuiescent) { + mDirty |= DIRTY_QUIESCENT; + updatePowerStateLocked(); + return; + } + wakePowerGroupLocked(mPowerGroups.get(Display.DEFAULT_DISPLAY_GROUP), eventTime, + reason, details, uid, opPackageName, uid); } - wakePowerGroupLocked(mPowerGroups.get(Display.DEFAULT_DISPLAY_GROUP), eventTime, - reason, details, uid, opPackageName, uid); + } finally { + Binder.restoreCallingIdentity(ident); } - } finally { - Binder.restoreCallingIdentity(ident); + }; + if (checkProximity) { + runWithProximityCheck(r); + } else { + r.run(); } } @@ -6116,7 +6195,7 @@ public void reboot(boolean confirm, @Nullable String reason, boolean wait) { ShutdownCheckPoints.recordCheckPoint(Binder.getCallingPid(), reason); final long ident = Binder.clearCallingIdentity(); try { - shutdownOrRebootInternal(HALT_MODE_REBOOT, confirm, reason, wait); + shutdownOrRebootInternal(HALT_MODE_REBOOT, confirm, reason, wait, false); } finally { Binder.restoreCallingIdentity(ident); } @@ -6136,7 +6215,29 @@ public void rebootSafeMode(boolean confirm, boolean wait) { ShutdownCheckPoints.recordCheckPoint(Binder.getCallingPid(), reason); final long ident = Binder.clearCallingIdentity(); try { - shutdownOrRebootInternal(HALT_MODE_REBOOT_SAFE_MODE, confirm, reason, wait); + shutdownOrRebootInternal(HALT_MODE_REBOOT_SAFE_MODE, confirm, reason, wait, false); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + /** + * Reboots the device with custom progress message. + * + * @param confirm If true, shows a reboot confirmation dialog. + * @param reason The reason for the reboot, or null if none. + * @param wait If true, this call waits for the reboot to complete and does not return. + */ + @Override // Binder call + public void rebootCustom(boolean confirm, String reason, boolean wait) { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.REBOOT, null); + if (PowerManager.REBOOT_RECOVERY.equals(reason)) { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.RECOVERY, null); + } + + final long ident = Binder.clearCallingIdentity(); + try { + shutdownOrRebootInternal(HALT_MODE_REBOOT, confirm, reason, wait, true); } finally { Binder.restoreCallingIdentity(ident); } @@ -6155,7 +6256,7 @@ public void shutdown(boolean confirm, String reason, boolean wait) { ShutdownCheckPoints.recordCheckPoint(Binder.getCallingPid(), reason); final long ident = Binder.clearCallingIdentity(); try { - shutdownOrRebootInternal(HALT_MODE_SHUTDOWN, confirm, reason, wait); + shutdownOrRebootInternal(HALT_MODE_SHUTDOWN, confirm, reason, wait, false); } finally { Binder.restoreCallingIdentity(ident); } @@ -6654,4 +6755,76 @@ static boolean isSameCallback(IWakeLockCallback callback1, } return false; } + + private void cleanupProximity() { + synchronized (mProximityWakeLock) { + cleanupProximityLocked(); + } + } + + private void cleanupProximityLocked() { + if (mProximityWakeLock.isHeld()) { + mProximityWakeLock.release(); + } + if (mProximityListener != null) { + mSensorManager.unregisterListener(mProximityListener); + mProximityListener = null; + } + } + + private void runWithProximityCheck(final Runnable r) { + if (mHandler.hasMessages(MSG_WAKE_UP)) { + // A message is already queued + return; + } + + final TelephonyManager tm = mContext.getSystemService(TelephonyManager.class); + final boolean hasIncomingCall = tm.getCallState() == TelephonyManager.CALL_STATE_RINGING; + + if (mProximityWakeSupported && mProximityWakeEnabled + && mProximitySensor != null && !hasIncomingCall) { + final Message msg = mHandler.obtainMessage(MSG_WAKE_UP); + msg.obj = r; + mHandler.sendMessageDelayed(msg, mProximityTimeOut); + runPostProximityCheck(r); + } else { + r.run(); + } + } + + private void runPostProximityCheck(final Runnable r) { + if (mSensorManager == null) { + r.run(); + return; + } + synchronized (mProximityWakeLock) { + mProximityWakeLock.acquire(); + mProximityListener = new SensorEventListener() { + @Override + public void onSensorChanged(SensorEvent event) { + cleanupProximityLocked(); + if (!mHandler.hasMessages(MSG_WAKE_UP)) { + Slog.w(TAG, "Proximity sensor took too long, " + + "wake event already triggered!"); + return; + } + mHandler.removeMessages(MSG_WAKE_UP); + final float distance = event.values[0]; + if (distance >= PROXIMITY_NEAR_THRESHOLD || + distance >= mProximitySensor.getMaximumRange()) { + r.run(); + } else { + Slog.w(TAG, "Not waking up. Proximity sensor is blocked."); + } + } + + @Override + public void onAccuracyChanged(Sensor sensor, int accuracy) { + // Do nothing + } + }; + mSensorManager.registerListener(mProximityListener, + mProximitySensor, SensorManager.SENSOR_DELAY_FASTEST); + } + } } diff --git a/services/core/java/com/android/server/power/ShutdownThread.java b/services/core/java/com/android/server/power/ShutdownThread.java index a82d4eaa5b28..cba1d40d5623 100644 --- a/services/core/java/com/android/server/power/ShutdownThread.java +++ b/services/core/java/com/android/server/power/ShutdownThread.java @@ -28,6 +28,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManagerInternal; +import android.content.res.Configuration; import android.media.AudioAttributes; import android.os.FileUtils; import android.os.Handler; @@ -77,7 +78,7 @@ public final class ShutdownThread extends Thread { private static final int MOUNT_SERVICE_STOP_PERCENT = 20; // length of vibration before shutting down - private static final int SHUTDOWN_VIBRATE_MS = 500; + private static final int SHUTDOWN_VIBRATE_MS = 250; // state tracking private static final Object sIsStartedGuard = new Object(); @@ -87,6 +88,7 @@ public final class ShutdownThread extends Thread { private static boolean mRebootSafeMode; private static boolean mRebootHasProgressBar; private static String mReason; + private static boolean mRebootCustom; // Provides shutdown assurance in case the system_server is killed public static final String SHUTDOWN_ACTION_PROPERTY = "sys.shutdown.requested"; @@ -149,6 +151,7 @@ public static void shutdown(final Context context, String reason, boolean confir mReboot = false; mRebootSafeMode = false; mReason = reason; + mRebootCustom = false; shutdownInner(context, confirm); } @@ -178,6 +181,11 @@ private static void shutdownInner(final Context context, boolean confirm) { ? com.android.internal.R.string.shutdown_confirm_question : com.android.internal.R.string.shutdown_confirm); + boolean isNightMode = (context.getResources().getConfiguration().uiMode + & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES; + int themeResId = isNightMode ? android.R.style.Theme_DeviceDefault_Dialog_Alert : + android.R.style.Theme_DeviceDefault_Light_Dialog_Alert; + Log.d(TAG, "Notifying thread to start shutdown longPressBehavior=" + longPressBehavior); if (confirm) { @@ -185,7 +193,7 @@ private static void shutdownInner(final Context context, boolean confirm) { if (sConfirmDialog != null) { sConfirmDialog.dismiss(); } - sConfirmDialog = new AlertDialog.Builder(context) + sConfirmDialog = new AlertDialog.Builder(context, themeResId) .setTitle(mRebootSafeMode ? com.android.internal.R.string.reboot_safemode_title : com.android.internal.R.string.power_off) @@ -242,6 +250,22 @@ public static void reboot(final Context context, String reason, boolean confirm) mRebootSafeMode = false; mRebootHasProgressBar = false; mReason = reason; + mRebootCustom = false; + shutdownInner(context, confirm); + } + + /** + * Request reboot system, reboot recovery or reboot bootloader + * + * @param context Context used to display the shutdown progress dialog. + * @param reason code to pass to the kernel (e.g. "recovery", "bootloader"), or null. + * @param confirm true if user confirmation is needed before rebooting. + */ + public static void rebootCustom(final Context context, String reason, boolean confirm) { + mReboot = true; + mRebootSafeMode = false; + mReason = reason; + mRebootCustom = true; shutdownInner(context, confirm); } @@ -263,6 +287,7 @@ public static void rebootSafeMode(final Context context, boolean confirm) { mRebootSafeMode = true; mRebootHasProgressBar = false; mReason = null; + mRebootCustom = false; shutdownInner(context, confirm); } @@ -323,15 +348,31 @@ private static ProgressDialog showShutdownDialog(Context context) { pd.setTitle(context.getText(com.android.internal.R.string.power_off)); pd.setMessage(context.getText(com.android.internal.R.string.shutdown_progress)); pd.setIndeterminate(true); - } else if (showSysuiReboot()) { - return null; } else { - // Factory reset path. Set the dialog message accordingly. - pd.setTitle(context.getText(com.android.internal.R.string.reboot_to_reset_title)); + if (showSysuiReboot()) { + return null; + } + pd.setTitle(context.getText(com.android.internal.R.string.reboot_to_recovery_title)); pd.setMessage(context.getText( - com.android.internal.R.string.reboot_to_reset_message)); + com.android.internal.R.string.reboot_to_recovery_message)); pd.setIndeterminate(true); } + } else if (mReason != null && PowerManager.REBOOT_BOOTLOADER.equals(mReason) && mRebootCustom) { + if (showSysuiReboot()) { + return null; + } + pd.setTitle(context.getText(com.android.internal.R.string.reboot_to_bootloader_title)); + pd.setMessage(context.getText( + com.android.internal.R.string.reboot_to_bootloader_message)); + pd.setIndeterminate(true); + } else if (mReason == null && mRebootCustom) { + if (showSysuiReboot()) { + return null; + } + pd.setTitle(context.getText(com.android.internal.R.string.reboot_system_title)); + pd.setMessage(context.getText( + com.android.internal.R.string.reboot_system_message)); + pd.setIndeterminate(true); } else { if (showSysuiReboot()) { return null; @@ -352,7 +393,7 @@ private static boolean showSysuiReboot() { try { StatusBarManagerInternal service = LocalServices.getService( StatusBarManagerInternal.class); - if (service.showShutdownUi(mReboot, mReason)) { + if (service.showShutdownUi(mReboot, mReason, mRebootCustom)) { // Sysui will handle shutdown UI. Log.d(TAG, "SysUI handling shutdown UI"); return true; diff --git a/services/core/java/com/android/server/power/ThermalManagerService.java b/services/core/java/com/android/server/power/ThermalManagerService.java index 96823c8ae9ab..722dc23ed5f2 100644 --- a/services/core/java/com/android/server/power/ThermalManagerService.java +++ b/services/core/java/com/android/server/power/ThermalManagerService.java @@ -139,17 +139,19 @@ private void onActivityManagerReady() { boolean halConnected = (mHalWrapper != null); if (!halConnected) { mHalWrapper = new ThermalHal20Wrapper(); + mHalWrapper.setCallback(this::onTemperatureChangedCallback); halConnected = mHalWrapper.connectToHal(); } if (!halConnected) { mHalWrapper = new ThermalHal11Wrapper(); + mHalWrapper.setCallback(this::onTemperatureChangedCallback); halConnected = mHalWrapper.connectToHal(); } if (!halConnected) { mHalWrapper = new ThermalHal10Wrapper(); + mHalWrapper.setCallback(this::onTemperatureChangedCallback); halConnected = mHalWrapper.connectToHal(); } - mHalWrapper.setCallback(this::onTemperatureChangedCallback); if (!halConnected) { Slog.w(TAG, "No Thermal HAL service on this device"); return; diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java index 690dd10aa3e5..9547a94498b8 100644 --- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java +++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java @@ -109,7 +109,7 @@ void appTransitionStarting(int displayId, long statusBarAnimationsStartTime, */ void setTopAppHidesStatusBar(boolean hidesStatusBar); - boolean showShutdownUi(boolean isReboot, String requestString); + boolean showShutdownUi(boolean isReboot, String requestString, boolean rebootCustom); /** * Show a rotation suggestion that a user may approve to rotate the screen. diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java index 653b51a95993..a9616c992616 100644 --- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java +++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java @@ -550,13 +550,13 @@ public void setTopAppHidesStatusBar(boolean hidesStatusBar) { } @Override - public boolean showShutdownUi(boolean isReboot, String reason) { + public boolean showShutdownUi(boolean isReboot, String reason, boolean rebootCustom) { if (!mContext.getResources().getBoolean(R.bool.config_showSysuiShutdown)) { return false; } if (mBar != null) { try { - mBar.showShutdownUi(isReboot, reason); + mBar.showShutdownUi(isReboot, reason, rebootCustom); return true; } catch (RemoteException ex) {} } @@ -958,6 +958,17 @@ public boolean isTracing() { return mTracingEnabled; } + @Override + public void toggleCameraFlash() { + if (mBar != null) { + try { + mBar.toggleCameraFlash(); + } catch (RemoteException ex) { + Slog.e(TAG, "Unable to toggle camera flash:", ex); + } + } + } + // TODO(b/117478341): make it aware of multi-display if needed. @Override public void disable(int what, IBinder token, String pkg) { @@ -1422,11 +1433,8 @@ public void shutdown() { * Allows the status bar to reboot the device. */ @Override - public void reboot(boolean safeMode) { + public void reboot(boolean safeMode, String reason) { enforceStatusBarService(); - String reason = safeMode - ? PowerManager.REBOOT_SAFE_MODE - : PowerManager.SHUTDOWN_USER_REQUESTED; ShutdownCheckPoints.recordCheckPoint(Binder.getCallingPid(), reason); final long identity = Binder.clearCallingIdentity(); try { @@ -1436,7 +1444,7 @@ public void reboot(boolean safeMode) { if (safeMode) { ShutdownThread.rebootSafeMode(getUiContext(), true); } else { - ShutdownThread.reboot(getUiContext(), reason, false); + ShutdownThread.rebootCustom(getUiContext(), reason, false); } }); } finally { diff --git a/services/core/java/com/android/server/storage/StorageSessionController.java b/services/core/java/com/android/server/storage/StorageSessionController.java index 9faf7a9155d2..f12d442f5b1a 100644 --- a/services/core/java/com/android/server/storage/StorageSessionController.java +++ b/services/core/java/com/android/server/storage/StorageSessionController.java @@ -85,6 +85,9 @@ public int getConnectionUserIdForVolume(VolumeInfo vol) { UserInfo userInfo = mUserManager.getUserInfo(vol.mountUserId); if (userInfo != null && isMediaSharedWithParent) { + if (userInfo.isParallel()) { + return userInfo.parallelParentId; + } // Clones use the same connection as their parent return userInfo.profileGroupId; } else { diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java index f888ff60b12a..83223976f2df 100644 --- a/services/core/java/com/android/server/trust/TrustManagerService.java +++ b/services/core/java/com/android/server/trust/TrustManagerService.java @@ -84,6 +84,7 @@ import com.android.internal.widget.LockPatternUtils; import com.android.server.LocalServices; import com.android.server.SystemService; +import com.android.server.app.AppLockManagerServiceInternal; import com.android.server.companion.virtual.VirtualDeviceManagerInternal; import org.xmlpull.v1.XmlPullParser; @@ -232,6 +233,8 @@ private enum TimeoutType { private boolean mTrustAgentsCanRun = false; private int mCurrentUser = UserHandle.USER_SYSTEM; + private AppLockManagerServiceInternal mAppLockManagerService = null; + public TrustManagerService(Context context) { super(context); mContext = context; @@ -602,9 +605,12 @@ && isTrustUsuallyManagedInternal(userId) != managed) { synchronized (mUserTrustState) { wasTrusted = (mUserTrustState.get(userId) == TrustState.TRUSTED); wasTrustable = (mUserTrustState.get(userId) == TrustState.TRUSTABLE); + boolean isAutomotive = getContext().getPackageManager().hasSystemFeature( + PackageManager.FEATURE_AUTOMOTIVE); boolean renewingTrust = wasTrustable && ( (flags & TrustAgentService.FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE) != 0); - boolean canMoveToTrusted = alreadyUnlocked || isFromUnlock || renewingTrust; + boolean canMoveToTrusted = + alreadyUnlocked || isFromUnlock || renewingTrust || isAutomotive; boolean upgradingTrustForCurrentUser = (userId == mCurrentUser); if (trustedByAtLeastOneAgent && wasTrusted) { @@ -687,7 +693,7 @@ public void unlockUserWithToken(long handle, byte[] token, int userId) { */ public void lockUser(int userId) { mLockPatternUtils.requireStrongAuth( - StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED, userId); + StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST, userId); try { WindowManagerGlobal.getWindowManagerService().lockNow(null); } catch (RemoteException e) { @@ -898,7 +904,7 @@ private void refreshDeviceLockedForUser(int userId, int unlockedUser) { boolean secure = mLockPatternUtils.isSecure(id); if (!info.supportsSwitchToByUser()) { - if (info.isManagedProfile() && !secure) { + if (info.isManagedProfile() && !secure || info.isParallel()) { setDeviceLockedForUser(id, false); } continue; @@ -928,7 +934,15 @@ private void refreshDeviceLockedForUser(int userId, int unlockedUser) { } setDeviceLockedForUser(id, deviceLocked); + getAppLockManagerService().notifyDeviceLocked(deviceLocked, id); + } + } + + private AppLockManagerServiceInternal getAppLockManagerService() { + if (mAppLockManagerService == null) { + mAppLockManagerService = LocalServices.getService(AppLockManagerServiceInternal.class); } + return mAppLockManagerService; } private void setDeviceLockedForUser(@UserIdInt int userId, boolean locked) { @@ -2084,7 +2098,7 @@ public void onAlarm() { if (mStrongAuthTracker.isTrustAllowedForUser(mUserId)) { if (DEBUG) Slog.d(TAG, "Revoking all trust because of trust timeout"); mLockPatternUtils.requireStrongAuth( - mStrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED, mUserId); + mStrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST, mUserId); } maybeLockScreen(mUserId); } diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java index f25929c36060..0e99894153cb 100644 --- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java +++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java @@ -1887,12 +1887,9 @@ public void onBootPhase(int phase) { } } - private static final HashMap sWallpaperType = new HashMap() { - { - put(FLAG_SYSTEM, RECORD_FILE); - put(FLAG_LOCK, RECORD_LOCK_FILE); - } - }; + private static final Map sWallpaperType = Map.of( + FLAG_SYSTEM, RECORD_FILE, + FLAG_LOCK, RECORD_LOCK_FILE); private void errorCheck(int userID) { sWallpaperType.forEach((type, filename) -> { diff --git a/services/core/java/com/android/server/webkit/SystemImpl.java b/services/core/java/com/android/server/webkit/SystemImpl.java index 68f554cb2758..5604fc6c0f39 100644 --- a/services/core/java/com/android/server/webkit/SystemImpl.java +++ b/services/core/java/com/android/server/webkit/SystemImpl.java @@ -219,7 +219,7 @@ private void enablePackageForUser(String packageName, boolean enable, int userId @Override public boolean systemIsDebuggable() { - return Build.IS_DEBUGGABLE; + return Build.IS_DEBUGGABLE && Build.IS_ENG; } @Override diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java index d2413f015003..6ae13a5421fe 100644 --- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java +++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java @@ -1,5 +1,6 @@ package com.android.server.wm; +import android.app.ActivityManager; import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT; import static android.app.ActivityManager.START_SUCCESS; import static android.app.ActivityManager.START_TASK_TO_FRONT; @@ -99,6 +100,7 @@ import android.os.Trace; import android.os.incremental.IncrementalManager; import android.util.ArrayMap; +import android.util.BoostFramework; import android.util.EventLog; import android.util.Log; import android.util.Slog; @@ -184,6 +186,10 @@ class ActivityMetricsLogger { private ArtManagerInternal mArtManagerInternal; private final StringBuilder mStringBuilder = new StringBuilder(); + public static BoostFramework mUxPerf = new BoostFramework(); + public static BoostFramework mPerfBoost = new BoostFramework(); + private static ActivityRecord mLaunchedActivity; + /** * Due to the global single concurrent launch sequence, all calls to this observer must be made * in-order on the same thread to fulfill the "happens-before" guarantee in LaunchObserver. @@ -1008,6 +1014,8 @@ private void logAppTransitionCancel(TransitionInfo info) { private void logAppTransitionFinished(@NonNull TransitionInfo info, boolean isHibernating) { if (DEBUG_METRICS) Slog.i(TAG, "logging finished transition " + info); + mLaunchedActivity = info.mLastLaunchedActivity; + // Take a snapshot of the transition info before sending it to the handler for logging. // This will avoid any races with other operations that modify the ActivityRecord. final TransitionInfoSnapshot infoSnapshot = new TransitionInfoSnapshot(info); @@ -1136,7 +1144,47 @@ private void logAppDisplayed(TransitionInfoSnapshot info) { sb.append(info.launchedActivityShortComponentName); sb.append(": "); TimeUtils.formatDuration(info.windowsDrawnDelayMs, sb); + + if (mPerfBoost != null) { + if (info.processRecord != null) { + mPerfBoost.perfHint(BoostFramework.VENDOR_HINT_FIRST_DRAW, info.packageName, + info.processRecord.getPid(), info.windowsDrawnDelayMs); + } + } + + if (mUxPerf != null) { + if (mUxPerf.board_first_api_lvl < BoostFramework.VENDOR_T_API_LEVEL && + mUxPerf.board_api_lvl < BoostFramework.VENDOR_T_API_LEVEL) { + mUxPerf.perfUXEngine_events(BoostFramework.UXE_EVENT_DISPLAYED_ACT, 0, info.packageName, info.windowsDrawnDelayMs); + } + } + Log.i(TAG, sb.toString()); + + if (mUxPerf != null) { + int isGame; + + if (ActivityManager.isLowRamDeviceStatic()) { + isGame = mLaunchedActivity.isAppInfoGame(); + } else { + isGame = (mUxPerf.perfGetFeedback(BoostFramework.VENDOR_FEEDBACK_WORKLOAD_TYPE, + mLaunchedActivity.packageName) == BoostFramework.WorkloadType.GAME) ? 1 : 0; + } + if (mLaunchedActivity.processName != null) { + if (!mLaunchedActivity.processName.equals(info.packageName)) { + isGame = 1; + } + } + if (mUxPerf.board_first_api_lvl < BoostFramework.VENDOR_T_API_LEVEL && + mUxPerf.board_api_lvl < BoostFramework.VENDOR_T_API_LEVEL) { + mUxPerf.perfUXEngine_events(BoostFramework.UXE_EVENT_GAME, 0, info.packageName, isGame); + } + } + + if (mLaunchedActivity.mPerf != null && mLaunchedActivity.perfActivityBoostHandler > 0) { + mLaunchedActivity.mPerf.perfLockReleaseHandler(mLaunchedActivity.perfActivityBoostHandler); + mLaunchedActivity.perfActivityBoostHandler = -1; + } } private void logRecentsAnimationLatency(TransitionInfo info) { diff --git a/services/core/java/com/android/server/wm/ActivityPluginDelegate.java b/services/core/java/com/android/server/wm/ActivityPluginDelegate.java new file mode 100644 index 000000000000..b2b08f2198d7 --- /dev/null +++ b/services/core/java/com/android/server/wm/ActivityPluginDelegate.java @@ -0,0 +1,174 @@ +/* + *Copyright (c) 2018, The Linux Foundation. All rights reserved. + * + *Redistribution and use in source and binary forms, with or without + *modification, are permitted provided that the following conditions are + *met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + *THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + *WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + *MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + *ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + *BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + *CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + *SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + *BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + *WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + *OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + *IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.android.server.wm; + +import dalvik.system.PathClassLoader; + +import java.io.File; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; + +import android.os.Environment; +import android.util.Log; +import android.content.Context; +import android.provider.Settings; +import android.app.ActivityThread; + +public class ActivityPluginDelegate { + + private static final String TAG = "ActivityPluginDelegate"; + private static final boolean LOGV = false; + + private static Class activityServiceClass = null; + private static Object activityServiceObj = null; + private static boolean extJarAvail = true; + + private static final String FOREGROUND_ACTIVITY_TRIGGER = + "foreground_activity_trigger"; + + //Try to get global settings for 15 times, if + //foreground_activity_trigger does not set to 1 after 15 times + //stop retry and foreground_activity_trigger is 0 + private static final int MAX_CONNECT_RETRIES = 15; + + static int mGetFeatureEnableRetryCount = MAX_CONNECT_RETRIES; + static boolean isEnabled = false; + + public static void activityInvokeNotification(String appName, + boolean isFullScreen) { + if (LOGV) Log.v(TAG, "activityInvokeNotification(" + + appName + ", " + isFullScreen + ")"); + if (!getFeatureFlag() || !extJarAvail || !loadActivityExtJar()) + return; + + try { + activityServiceClass.getMethod("sendActivityInvokeNotification", + String.class, boolean.class).invoke( + activityServiceObj, appName, isFullScreen); + } catch (InvocationTargetException | + SecurityException | NoSuchMethodException e) { + if (LOGV) { + Log.w(TAG, "Failed to invoke activityInvokeNotification: " + e); + e.printStackTrace(); + } + } catch (Exception e) { + if (LOGV) { + Log.w(TAG, "Error calling activityInvokeNotification"+ + "Method on ActivityExt jar: " + e); + e.printStackTrace(); + } + } + } + + public static void activitySuspendNotification(String appName, + boolean isFullScreen, + boolean isBg) { + if (LOGV) Log.v(TAG, "activitySuspendNotification(" + + appName + ", " + isFullScreen + ", " + isBg + ")"); + if (!getFeatureFlag() || !extJarAvail || !loadActivityExtJar()) + return; + + try { + activityServiceClass.getMethod("sendActivitySuspendNotification", + String.class, boolean.class, boolean.class).invoke( + activityServiceObj, appName, isFullScreen, isBg); + } catch (InvocationTargetException | + SecurityException | NoSuchMethodException e) { + if (LOGV) { + Log.w(TAG, "Failed to call sendActivitySuspendNotification: " + e); + e.printStackTrace(); + } + } catch (Exception e) { + if (LOGV) { + Log.w(TAG, "Error calling sendActivitySuspendNotification"+ + "Method on ActivityExt jar: " + e); + e.printStackTrace(); + } + } + } + + private static synchronized boolean loadActivityExtJar() { + final String realProvider = "com.qualcomm.qti."+ + "activityextension.ActivityNotifier"; + final String realProviderPath = Environment.getSystemExtDirectory(). + getAbsolutePath() + "/framework/ActivityExt.jar"; + + if (activityServiceClass != null && activityServiceObj != null) { + return true; + } + + if ((extJarAvail = new File(realProviderPath).exists()) == false) { + if (LOGV) Log.w(TAG, "ActivityExt jar file not present"); + return extJarAvail; + } + + if (activityServiceClass == null && activityServiceObj == null) { + if (LOGV) Log.v(TAG, "loading ActivityExt jar"); + try { + PathClassLoader classLoader = new PathClassLoader + (realProviderPath, ClassLoader.getSystemClassLoader()); + + activityServiceClass = classLoader.loadClass(realProvider); + activityServiceObj = activityServiceClass.newInstance(); + if (LOGV) Log.v(TAG, "ActivityExt jar loaded"); + } catch (ClassNotFoundException | + InstantiationException | IllegalAccessException e) { + if (LOGV) { + Log.w(TAG, "Failed to find, instantiate or access ActivityExt jar:" + e); + e.printStackTrace(); + } + extJarAvail = false; + return false; + } catch (Exception e) { + if (LOGV) { + Log.w(TAG, "unable to load ActivityExt jar:" + e); + e.printStackTrace(); + } + extJarAvail = false; + return false; + } + } + return true; + } + + public static synchronized boolean getFeatureFlag() { + //Global setting has been enabled for foreground_activity_trigger + //Or no one sets foreground_activity_trigger after all retry + //No need to invoke Settings API + if(isEnabled == true || (mGetFeatureEnableRetryCount == 0)) { + return isEnabled; + } + isEnabled = ((Settings.Global.getInt(ActivityThread.currentApplication(). + getApplicationContext().getContentResolver(), + FOREGROUND_ACTIVITY_TRIGGER, 1)) == 1); + --mGetFeatureEnableRetryCount; + return isEnabled; + } +} diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index b8486e7aa2b4..a6ef0ee2d727 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -299,11 +299,13 @@ import android.os.Process; import android.os.RemoteException; import android.os.SystemClock; +import android.os.SystemProperties; import android.os.Trace; import android.os.UserHandle; import android.service.contentcapture.ActivityEvent; import android.service.dreams.DreamActivity; import android.service.voice.IVoiceInteractionSession; +import android.util.BoostFramework; import android.util.ArraySet; import android.util.EventLog; import android.util.Log; @@ -380,7 +382,7 @@ /** * An entry in the history task, representing an activity. */ -final class ActivityRecord extends WindowToken implements WindowManagerService.AppFreezeListener { +public final class ActivityRecord extends WindowToken implements WindowManagerService.AppFreezeListener { private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityRecord" : TAG_ATM; private static final String TAG_ADD_REMOVE = TAG + POSTFIX_ADD_REMOVE; private static final String TAG_APP = TAG + POSTFIX_APP; @@ -444,7 +446,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A final int mUserId; // The package implementing intent's component // TODO: rename to mPackageName - final String packageName; + public final String packageName; // the intent component, or target of an alias. final ComponentName mActivityComponent; // Input application handle used by the input dispatcher. @@ -472,6 +474,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A private int labelRes; // the label information from the package mgr. private int icon; // resource identifier of activity's icon. private int theme; // resource identifier of activity's theme. + public int perfActivityBoostHandler = -1; //perflock handler when activity is created. private Task task; // the task this is in. private long createTime = System.currentTimeMillis(); long lastVisibleTime; // last time this activity became visible @@ -532,6 +535,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // process that it is hidden. private boolean mLastDeferHidingClient; // If true we will defer setting mClientVisible to false // and reporting to the client that it is hidden. + public boolean launching; // is activity launch in progress? + public boolean translucentWindowLaunch; // a translucent window launch? boolean nowVisible; // is this activity's window visible? boolean mClientVisibilityDeferred;// was the visibility change message to client deferred? boolean idle; // has the activity gone idle? @@ -624,6 +629,12 @@ enum State { boolean pendingVoiceInteractionStart; // Waiting for activity-invoked voice session IVoiceInteractionSession voiceSession; // Voice interaction session for this activity + public BoostFramework mPerf = null; + public BoostFramework mPerf_iop = null; + + private final boolean isLowRamDevice = + SystemProperties.getBoolean("ro.config.low_ram", false); + boolean mVoiceInteraction; private int mPendingRelaunchCount; @@ -2054,6 +2065,8 @@ private ActivityRecord(ActivityTaskManagerService _service, WindowProcessControl super.setClientVisible(true); idle = false; hasBeenLaunched = false; + launching = false; + translucentWindowLaunch = false; mTaskSupervisor = supervisor; info.taskAffinity = computeTaskAffinity(info.taskAffinity, info.applicationInfo.uid, @@ -2138,6 +2151,9 @@ private ActivityRecord(ActivityTaskManagerService _service, WindowProcessControl mActivityRecordInputSink = new ActivityRecordInputSink(this, sourceRecord); updateEnterpriseThumbnailDrawable(mAtmService.getUiContext()); + + if (mPerf == null) + mPerf = new BoostFramework(); } /** @@ -2307,6 +2323,7 @@ private boolean validateStartingWindowTheme(ActivityRecord prev, String pkg, int windowDisableStarting); // If this activity is launched from system surface, ignore windowDisableStarting if (windowIsTranslucent || windowIsFloating) { + translucentWindowLaunch = true; return false; } if (windowShowWallpaper @@ -3693,6 +3710,7 @@ boolean destroyIfPossible(String reason) { } makeFinishingLocked(); + getRootTask().onARStopTriggered(this); final boolean activityRemoved = destroyImmediately("finish-imm:" + reason); // If the display does not have running activity, the configuration may need to be @@ -6106,6 +6124,11 @@ void completeResumeLocked() { if (isActivityTypeHome()) { mTaskSupervisor.updateHomeProcess(task.getBottomMostActivity().app); + try { + mTaskSupervisor.new PreferredAppsTask().execute(); + } catch (Exception e) { + Slog.v (TAG, "Exception: " + e); + } } if (nowVisible) { @@ -6202,6 +6225,7 @@ void removeTimeouts() { void stopIfPossible() { if (DEBUG_SWITCH) Slog.d(TAG_SWITCH, "Stopping: " + this); + launching = false; final Task rootTask = getRootTask(); if (isNoHistory()) { if (!finishing) { @@ -6228,6 +6252,7 @@ void stopIfPossible() { ProtoLog.v(WM_DEBUG_STATES, "Moving to STOPPING: %s (stop requested)", this); setState(STOPPING, "stopIfPossible"); + getRootTask().onARStopTriggered(this); if (DEBUG_VISIBILITY) { Slog.v(TAG_VISIBILITY, "Stopping:" + this); } @@ -6569,6 +6594,12 @@ void onStartingWindowDrawn() { /** Called when the windows associated app window container are drawn. */ private void onWindowsDrawn(long timestampNs) { + if (mPerf != null && perfActivityBoostHandler > 0) { + mPerf.perfLockReleaseHandler(perfActivityBoostHandler); + perfActivityBoostHandler = -1; + } else if (perfActivityBoostHandler > 0) { + Slog.w(TAG, "activity boost didn't release as expected"); + } final TransitionInfoSnapshot info = mTaskSupervisor .getActivityMetricsLogger().notifyWindowsDrawn(this, timestampNs); final boolean validInfo = info != null; @@ -6598,6 +6629,7 @@ void onWindowsVisible() { if (DEBUG_SWITCH) Log.v(TAG_SWITCH, "windowsVisibleLocked(): " + this); if (!nowVisible) { nowVisible = true; + launching = false; lastVisibleTime = SystemClock.uptimeMillis(); mAtmService.scheduleAppGcsLocked(); // The nowVisible may be false in onAnimationFinished because the transition animation @@ -6622,6 +6654,7 @@ void onWindowsGone() { if (DEBUG_VISIBILITY) Slog.v(TAG_WM, "Reporting gone in " + token); if (DEBUG_SWITCH) Log.v(TAG_SWITCH, "windowsGone(): " + this); nowVisible = false; + launching = false; } @Override @@ -7310,6 +7343,15 @@ boolean shouldAnimate() { return task == null || task.shouldAnimate(); } + public int isAppInfoGame() { + int isGame = 0; + if (info.applicationInfo != null) { + isGame = (info.applicationInfo.category == ApplicationInfo.CATEGORY_GAME || + (info.applicationInfo.flags & ApplicationInfo.FLAG_IS_GAME) == ApplicationInfo.FLAG_IS_GAME) ? 1 : 0; + } + return isGame; + } + /** * Creates a layer to apply crop to an animation. */ diff --git a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java index 7d84bdf78056..a9c0defe5c3a 100644 --- a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java +++ b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java @@ -62,6 +62,7 @@ import com.android.internal.app.UnlaunchableAppActivity; import com.android.server.LocalServices; import com.android.server.am.ActivityManagerService; +import com.android.server.app.AppLockManagerServiceInternal; import com.android.server.wm.ActivityInterceptorCallback.ActivityInterceptResult; /** @@ -219,6 +220,9 @@ boolean intercept(Intent intent, ResolveInfo rInfo, ActivityInfo aInfo, String r if (interceptLockedManagedProfileIfNeeded()) { return true; } + if (interceptLockedAppIfNeeded()) { + return true; + } final SparseArray callbacks = mService.getActivityInterceptorCallbacks(); @@ -468,4 +472,32 @@ private ActivityInterceptorCallback.ActivityInterceptorInfo getInterceptorInfo( mRInfo, mAInfo, mResolvedType, mCallingPid, mCallingUid, mActivityOptions, clearOptionsAnimation); } + + private AppLockManagerServiceInternal getAppLockManagerService() { + return mService.getAppLockManagerService(); + } + + private boolean interceptLockedAppIfNeeded() { + if (getAppLockManagerService() == null) return false; + final Intent interceptingIntent = getAppLockManagerService().interceptActivity(getInterceptorInfo(null)); + if (interceptingIntent == null) return false; + mIntent = interceptingIntent; + mCallingPid = mRealCallingPid; + mCallingUid = mRealCallingUid; + mResolvedType = null; + // If we are intercepting and there was a task, convert it into an extra for the + // ConfirmCredentials intent and unassign it, as otherwise the task will move to + // front even if ConfirmCredentials is cancelled. + if (mInTask != null) { + mIntent.putExtra(EXTRA_TASK_ID, mInTask.mTaskId); + mInTask = null; + } + if (mActivityOptions == null) { + mActivityOptions = ActivityOptions.makeBasic(); + } + + mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, mUserId, 0, mRealCallingUid); + mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags, null /*profilerInfo*/); + return true; + } } diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index 027d485d15a0..bada5b11e712 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -121,6 +121,7 @@ import android.os.UserManager; import android.service.voice.IVoiceInteractionSession; import android.text.TextUtils; +import android.util.BoostFramework; import android.util.ArraySet; import android.util.DebugUtils; import android.util.Pools.SynchronizedPool; @@ -141,6 +142,8 @@ import com.android.server.wm.LaunchParamsController.LaunchParams; import com.android.server.wm.TaskFragment.EmbeddingCheckResult; +import ink.kaleidoscope.server.ParallelSpaceManagerService; + import java.io.PrintWriter; import java.text.DateFormat; import java.util.Date; @@ -235,6 +238,8 @@ class ActivityStarter { private IVoiceInteractionSession mVoiceSession; private IVoiceInteractor mVoiceInteractor; + public BoostFramework mPerf = null; + // Last activity record we attempted to start private ActivityRecord mLastStartActivityRecord; // The result of the last activity we attempted to start. @@ -589,6 +594,7 @@ void resolveActivity(ActivityTaskSupervisor supervisor) { mSupervisor = supervisor; mInterceptor = interceptor; reset(true); + mPerf = new BoostFramework(); } /** @@ -656,6 +662,12 @@ boolean relatedToPackage(String packageName) { */ int execute() { try { + if (ParallelSpaceManagerService.isCurrentParallelUser(mRequest.userId) && + Intent.ACTION_MAIN.equals(mRequest.intent.getAction()) && + mRequest.intent.hasCategory(Intent.CATEGORY_HOME)) { + mRequest.userId = ParallelSpaceManagerService.getCurrentParallelOwnerId(); + } + onExecutionStarted(); // Refuse possible leaked file descriptors @@ -3008,6 +3020,26 @@ private void deliverNewIntent(ActivityRecord activity, NeededUriGrants intentGra /** Places {@link #mStartActivity} in {@code task} or an embedded {@link TaskFragment}. */ private void addOrReparentStartingActivity(@NonNull Task task, String reason) { + String packageName= mService.mContext.getPackageName(); + if (mPerf != null) { + if (mPerf.getPerfHalVersion() >= BoostFramework.PERF_HAL_V23) { + int pkgType = + mPerf.perfGetFeedback(BoostFramework.VENDOR_FEEDBACK_WORKLOAD_TYPE, + packageName); + mStartActivity.perfActivityBoostHandler = + mPerf.perfHintAcqRel(mStartActivity.perfActivityBoostHandler, + BoostFramework.VENDOR_HINT_FIRST_LAUNCH_BOOST, packageName, + -1, BoostFramework.Launch.ACTIVITY_LAUNCH_BOOST, 1, pkgType); + } else { + if (mStartActivity.perfActivityBoostHandler > 0) { + Slog.i(TAG, "Activity boosted, release it firstly"); + mPerf.perfLockReleaseHandler(mStartActivity.perfActivityBoostHandler); + } + mStartActivity.perfActivityBoostHandler = + mPerf.perfHint(BoostFramework.VENDOR_HINT_FIRST_LAUNCH_BOOST, + packageName, -1, BoostFramework.Launch.BOOST_V1); + } + } TaskFragment newParent = task; if (mInTaskFragment != null) { int embeddingCheckResult = canEmbedActivity(mInTaskFragment, mStartActivity, task); diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java index e7b62b0d66d3..07cf699997e5 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java @@ -710,4 +710,6 @@ public abstract void registerActivityStartInterceptor( */ public abstract void restartTaskActivityProcessIfVisible( int taskId, @NonNull String packageName); + + public abstract boolean isVisibleActivity(IBinder activityToken); } diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 0398cc84f9db..58565f392152 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -262,6 +262,7 @@ import com.android.server.am.PendingIntentController; import com.android.server.am.PendingIntentRecord; import com.android.server.am.UserState; +import com.android.server.app.AppLockManagerServiceInternal; import com.android.server.firewall.IntentFirewall; import com.android.server.pm.UserManagerService; import com.android.server.policy.PermissionPolicyInternal; @@ -378,7 +379,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { * @see WindowManagerThreadPriorityBooster */ final Object mGlobalLockWithoutBoost = mGlobalLock; - ActivityTaskSupervisor mTaskSupervisor; + public ActivityTaskSupervisor mTaskSupervisor; ActivityClientController mActivityClientController; RootWindowContainer mRootWindowContainer; WindowManagerService mWindowManager; @@ -832,6 +833,8 @@ public void run() { } }; + private AppLockManagerServiceInternal mAppLockManagerService = null; + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) public ActivityTaskManagerService(Context context) { mContext = context; @@ -1799,6 +1802,7 @@ public final int startActivityFromRecents(int taskId, Bundle bOptions) { final int callingPid = Binder.getCallingPid(); final int callingUid = Binder.getCallingUid(); + final SafeActivityOptions safeOptions = SafeActivityOptions.fromBundle(bOptions); final long origId = Binder.clearCallingIdentity(); try { @@ -3749,6 +3753,15 @@ public TaskSnapshot takeTaskSnapshot(int taskId) { Slog.w(TAG, "takeTaskSnapshot: taskId=" + taskId + " not found or not visible"); return null; } + final Task rootTask = task.getRootTask(); + final String packageName = + rootTask != null && rootTask.realActivity != null + ? rootTask.realActivity.getPackageName() + : null; + if (packageName != null && getAppLockManagerService().requireUnlock( + packageName, task.mUserId)) { + return null; + } return mWindowManager.mTaskSnapshotController.captureTaskSnapshot( task, false /* snapshotHome */); } @@ -5119,6 +5132,13 @@ StatusBarManagerInternal getStatusBarManagerInternal() { return mStatusBarManagerInternal; } + AppLockManagerServiceInternal getAppLockManagerService() { + if (mAppLockManagerService == null) { + mAppLockManagerService = LocalServices.getService(AppLockManagerServiceInternal.class); + } + return mAppLockManagerService; + } + AppWarnings getAppWarningsLocked() { return mAppWarnings; } @@ -6877,5 +6897,13 @@ public void restartTaskActivityProcessIfVisible(int taskId, String packageName) activity.restartProcessIfVisible(); } } + + @Override + public boolean isVisibleActivity(IBinder activityToken) { + synchronized (mGlobalLock) { + final ActivityRecord r = ActivityRecord.isInRootTaskLocked(activityToken); + return r != null && r.isInterestingToUserLocked(); + } + } } } diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java index a870b8afe2f9..5162eb6b8732 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java @@ -49,6 +49,7 @@ import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STATES; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_TASKS; +import static com.android.server.wm.ActivityRecord.State.DESTROYED; import static com.android.server.wm.ActivityRecord.State.PAUSED; import static com.android.server.wm.ActivityRecord.State.PAUSING; import static com.android.server.wm.ActivityRecord.State.RESTARTING_PROCESS; @@ -131,6 +132,8 @@ import android.util.Slog; import android.util.SparseArray; import android.util.SparseIntArray; +import android.util.BoostFramework; +import com.android.internal.app.procstats.ProcessStats; import android.view.Display; import com.android.internal.R; @@ -154,6 +157,9 @@ import java.util.List; import java.util.function.Consumer; +import java.util.Arrays; +import android.os.AsyncTask; + // TODO: This class has become a dumping ground. Let's // - Move things relating to the hierarchy to RootWindowContainer // - Move things relating to activity life cycles to maybe a new class called ActivityLifeCycler @@ -177,6 +183,12 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { // How long we can hold the launch wake lock before giving up. private static final int LAUNCH_TIMEOUT = 10 * 1000 * Build.HW_TIMEOUT_MULTIPLIER; + public static boolean mPerfSendTapHint = false; + public static boolean mIsPerfBoostAcquired = false; + public static int mPerfHandle = -1; + public BoostFramework mPerfBoost = new BoostFramework(); + public BoostFramework mUxPerf = new BoostFramework(); + /** How long we wait until giving up on the activity telling us it released the top state. */ private static final int TOP_RESUMED_STATE_LOSS_TIMEOUT = 500; @@ -236,7 +248,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { private static final int MAX_TASK_IDS_PER_USER = UserHandle.PER_USER_RANGE; final ActivityTaskManagerService mService; - RootWindowContainer mRootWindowContainer; + public RootWindowContainer mRootWindowContainer; /** The historial list of recent tasks including inactive tasks */ RecentTasks mRecentTasks; @@ -755,7 +767,7 @@ ResolveInfo resolveIntent(Intent intent, String resolvedType, int userId, int fl } } - ActivityInfo resolveActivity(Intent intent, String resolvedType, int startFlags, + public ActivityInfo resolveActivity(Intent intent, String resolvedType, int startFlags, ProfilerInfo profilerInfo, int userId, int filterCallingUid) { final ResolveInfo rInfo = resolveIntent(intent, resolvedType, userId, 0, filterCallingUid); return resolveActivity(intent, rInfo, startFlags, profilerInfo); @@ -1041,6 +1053,10 @@ void startSpecificActivity(ActivityRecord r, boolean andResume, boolean checkCon boolean knownToBeDead = false; if (wpc != null && wpc.hasThread()) { try { + if (mPerfBoost != null) { + Slog.i(TAG, "The Process " + r.processName + " Already Exists in BG. So sending its PID: " + wpc.getPid()); + mPerfBoost.perfHint(BoostFramework.VENDOR_HINT_FIRST_LAUNCH_BOOST, r.processName, wpc.getPid(), BoostFramework.Launch.TYPE_START_APP_FROM_BG); + } realStartActivityLocked(r, wpc, andResume, checkConfig); return; } catch (RemoteException e) { @@ -1424,6 +1440,16 @@ void activityIdleInternal(ActivityRecord r, boolean fromTimeout, void findTaskToMoveToFront(Task task, int flags, ActivityOptions options, String reason, boolean forceNonResizeable) { Task currentRootTask = task.getRootTask(); + + Task focusedStack = mRootWindowContainer.getTopDisplayFocusedRootTask(); + ActivityRecord top_activity = focusedStack != null ? focusedStack.getTopNonFinishingActivity() : null; + + //top_activity = task.stack.topRunningActivityLocked(); + /* App is launching from recent apps and it's a new process */ + if((top_activity != null) && (top_activity.getState() == DESTROYED)) { + acquireAppLaunchPerfLock(top_activity); + } + if (currentRootTask == null) { Slog.e(TAG, "findTaskToMoveToFront: can't move task=" + task + " to front. Root task is null"); @@ -1701,6 +1727,15 @@ void cleanUpRemovedTaskLocked(Task task, boolean killProcess, boolean removeFrom ActivityManagerInternal::killProcessesForRemovedTask, mService.mAmInternal, procsToKill); mService.mH.sendMessage(m); + + } + + public void startPreferredApps() { + try { + new PreferredAppsTask().execute(); + } catch (Exception e) { + Slog.v (TAG, "Exception while calling PreferredAppsTask: " + e); + } } /** @@ -1855,6 +1890,81 @@ boolean shutdownLocked(int timeout) { return timedout; } + void acquireAppLaunchPerfLock(ActivityRecord r) { + /* Acquire perf lock during new app launch */ + if (mPerfBoost != null) { + + int pkgType = mPerfBoost.perfGetFeedback(BoostFramework.VENDOR_FEEDBACK_WORKLOAD_TYPE, + r.packageName); + int wpcPid = -1; + if (mService != null && r != null && r.info != null && r.info.applicationInfo !=null) { + final WindowProcessController wpc = + mService.getProcessController(r.processName, r.info.applicationInfo.uid); + if (wpc != null && wpc.hasThread()) { + //If target process didn't start yet, this operation will be done when app call attach + wpcPid = wpc.getPid(); + } + } + if (mPerfBoost.getPerfHalVersion() >= BoostFramework.PERF_HAL_V23) { + mPerfBoost.perfHintAcqRel(-1, BoostFramework.VENDOR_HINT_FIRST_LAUNCH_BOOST, + r.packageName, -1, BoostFramework.Launch.BOOST_V1, 2, pkgType, wpcPid); + mPerfSendTapHint = true; + mPerfBoost.perfHintAcqRel(-1, BoostFramework.VENDOR_HINT_FIRST_LAUNCH_BOOST, + r.packageName, -1, BoostFramework.Launch.BOOST_V2, 2, pkgType, wpcPid); + if (wpcPid != -1) { + mPerfBoost.perfHintAcqRel(-1, BoostFramework.VENDOR_HINT_FIRST_LAUNCH_BOOST, + r.packageName, wpcPid, BoostFramework.Launch.TYPE_ATTACH_APPLICATION, + 2, pkgType, wpcPid); + } + + if (pkgType == BoostFramework.WorkloadType.GAME) + { + mPerfHandle = + mPerfBoost.perfHintAcqRel(-1, BoostFramework.VENDOR_HINT_FIRST_LAUNCH_BOOST, + r.packageName, -1, BoostFramework.Launch.BOOST_GAME, 2, pkgType, wpcPid); + } else { + mPerfHandle = + mPerfBoost.perfHintAcqRel(-1, BoostFramework.VENDOR_HINT_FIRST_LAUNCH_BOOST, + r.packageName, -1, BoostFramework.Launch.BOOST_V3, 2, pkgType, wpcPid); + } + + } else { + mPerfBoost.perfHint(BoostFramework.VENDOR_HINT_FIRST_LAUNCH_BOOST, r.packageName, + -1, BoostFramework.Launch.BOOST_V1); + mPerfSendTapHint = true; + mPerfBoost.perfHint(BoostFramework.VENDOR_HINT_FIRST_LAUNCH_BOOST, r.packageName, + -1, BoostFramework.Launch.BOOST_V2); + if (wpcPid != -1) { + mPerfBoost.perfHint(BoostFramework.VENDOR_HINT_FIRST_LAUNCH_BOOST, + r.packageName, wpcPid, BoostFramework.Launch.TYPE_ATTACH_APPLICATION); + } + + if (pkgType == BoostFramework.WorkloadType.GAME) + { + mPerfHandle = mPerfBoost.perfHint(BoostFramework.VENDOR_HINT_FIRST_LAUNCH_BOOST, + r.packageName, -1, BoostFramework.Launch.BOOST_GAME); + } else { + mPerfHandle = mPerfBoost.perfHint(BoostFramework.VENDOR_HINT_FIRST_LAUNCH_BOOST, + r.packageName, -1, BoostFramework.Launch.BOOST_V3); + } + } + if (mPerfHandle > 0) + mIsPerfBoostAcquired = true; + // Start IOP + if (r.info.applicationInfo != null && r.info.applicationInfo.sourceDir != null) { + if (mPerfBoost.board_first_api_lvl < BoostFramework.VENDOR_T_API_LEVEL && + mPerfBoost.board_api_lvl < BoostFramework.VENDOR_T_API_LEVEL) { + mPerfBoost.perfIOPrefetchStart(-1,r.packageName, + r.info.applicationInfo.sourceDir.substring(0, r.info.applicationInfo.sourceDir.lastIndexOf('/'))); + } + } + } + } + + public ActivityRecord getTopResumedActivity() { + return mTopResumedActivity; + } + void comeOutOfSleepIfNeededLocked() { removeSleepTimeouts(); if (mGoingToSleepWakeLock.isHeld()) { @@ -2704,4 +2814,42 @@ void dump(PrintWriter pw, String prefix) { mResult.dump(pw, prefix + " "); } } + + class PreferredAppsTask extends AsyncTask { + @Override + protected Void doInBackground(Void... params) { + String res = null; + final Intent intent = new Intent(Intent.ACTION_MAIN); + int trimLevel = 0; + try { + trimLevel = ActivityManager.getService().getMemoryTrimLevel(); + } catch (RemoteException e) { + return null; + } + if (mUxPerf != null + && trimLevel < ProcessStats.ADJ_MEM_FACTOR_CRITICAL) { + if (mUxPerf.board_first_api_lvl < BoostFramework.VENDOR_T_API_LEVEL && + mUxPerf.board_api_lvl < BoostFramework.VENDOR_T_API_LEVEL) { + res = mUxPerf.perfUXEngine_trigger(BoostFramework.UXE_TRIGGER); + } else { + res = mUxPerf.perfSyncRequest(BoostFramework.VENDOR_FEEDBACK_PA_FW); + } + if (res == null) + return null; + String[] p_apps = res.trim().split("/"); + if (p_apps.length != 0) { + ArrayList apps_l = new ArrayList(Arrays.asList(p_apps)); + Bundle bParams = new Bundle(); + if (bParams == null) + return null; + bParams.putStringArrayList("start_empty_apps", apps_l); + final Message msg = PooledLambda.obtainMessage( + ActivityManagerInternal::startActivityAsUserEmpty, mService.mAmInternal, bParams); + mService.mH.sendMessage(msg); + } + } + return null; + } + } + } diff --git a/services/core/java/com/android/server/wm/AppTaskImpl.java b/services/core/java/com/android/server/wm/AppTaskImpl.java index e80c2607a0ad..0bfc48b4b54c 100644 --- a/services/core/java/com/android/server/wm/AppTaskImpl.java +++ b/services/core/java/com/android/server/wm/AppTaskImpl.java @@ -98,7 +98,7 @@ public ActivityManager.RecentTaskInfo getTaskInfo() { throw new IllegalArgumentException("Unable to find task ID " + mTaskId); } return mService.getRecentTasks().createRecentTaskInfo(task, - false /* stripExtras */); + false /* stripExtras */, true /* getTasksAllowed */); } finally { Binder.restoreCallingIdentity(origId); } diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index 4c69f87106d1..a00b47905d96 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -16,8 +16,11 @@ package com.android.server.wm; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; +import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; +import static android.view.Display.INVALID_DISPLAY; import static android.view.Display.TYPE_INTERNAL; import static android.view.InsetsState.ITYPE_BOTTOM_MANDATORY_GESTURES; import static android.view.InsetsState.ITYPE_BOTTOM_TAPPABLE_ELEMENT; @@ -95,11 +98,13 @@ import android.annotation.Nullable; import android.annotation.Px; import android.app.ActivityManager; +import android.app.ActivityTaskManager; import android.app.ActivityThread; import android.app.LoadedApk; import android.app.ResourcesManager; import android.content.Context; import android.content.Intent; +import android.content.pm.ApplicationInfo; import android.content.res.Resources; import android.graphics.Insets; import android.graphics.PixelFormat; @@ -114,6 +119,7 @@ import android.os.SystemClock; import android.os.SystemProperties; import android.os.UserHandle; +import android.util.BoostFramework; import android.util.ArraySet; import android.util.PrintWriterPrinter; import android.util.Slog; @@ -208,6 +214,19 @@ public class DisplayPolicy { private final ImmersiveModeConfirmation mImmersiveModeConfirmation; private final ScreenshotHelper mScreenshotHelper; + private static boolean SCROLL_BOOST_SS_ENABLE = false; + private static boolean DRAG_PLH_ENABLE = false; + private static boolean isLowRAM = false; + + /* + * @hide + */ + BoostFramework mPerfBoostDrag = null; + BoostFramework mPerfBoostFling = null; + BoostFramework mPerfBoostPrefling = null; + BoostFramework mPerf = new BoostFramework(); + private boolean mIsPerfBoostFlingAcquired; + private final Object mServiceAcquireLock = new Object(); private StatusBarManagerInternal mStatusBarManagerInternal; @@ -417,6 +436,38 @@ public void handleMessage(Message msg) { } } + private String getAppPackageName() { + String currentPackage; + try { + ActivityManager.RunningTaskInfo rti = ActivityTaskManager.getService().getTasks( + 1, false /* filterVisibleRecents */, false /*keepIntentExtra */, INVALID_DISPLAY /*don't filter display */).get(0); + currentPackage = rti.topActivity.getPackageName(); + } catch (Exception e) { + currentPackage = null; + } + return currentPackage; + } + + private boolean isTopAppGame(String currentPackage, BoostFramework BoostType) { + boolean isGame = false; + if (isLowRAM) { + try { + ApplicationInfo ai = mContext.getPackageManager().getApplicationInfo(currentPackage, 0); + if(ai != null) { + isGame = (ai.category == ApplicationInfo.CATEGORY_GAME) || + ((ai.flags & ApplicationInfo.FLAG_IS_GAME) == + ApplicationInfo.FLAG_IS_GAME); + } + } catch (Exception e) { + return false; + } + } else { + isGame = (BoostType.perfGetFeedback(BoostFramework.VENDOR_FEEDBACK_WORKLOAD_TYPE, + currentPackage) == BoostFramework.WorkloadType.GAME); + } + return isGame; + } + DisplayPolicy(WindowManagerService service, DisplayContent displayContent) { mService = service; mContext = displayContent.isDefaultDisplay ? service.mContext @@ -445,6 +496,12 @@ public void handleMessage(Message msg) { mScreenOnFully = true; } + if (mPerf != null) { + SCROLL_BOOST_SS_ENABLE = Boolean.parseBoolean(mPerf.perfGetProp("vendor.perf.gestureflingboost.enable", "true")); + DRAG_PLH_ENABLE = Boolean.parseBoolean(mPerf.perfGetProp("ro.vendor.perf.dplh", "false")); + } + isLowRAM = SystemProperties.getBoolean("ro.config.low_ram", false); + final Looper looper = UiThread.getHandler().getLooper(); mHandler = new PolicyHandler(looper); // TODO(b/181821798) Migrate SystemGesturesPointerEventListener to use window context. @@ -525,6 +582,100 @@ public void onFling(int duration) { } } + @Override + public void onVerticalFling(int duration) { + String currentPackage = getAppPackageName(); + if (currentPackage == null) { + Slog.e(TAG, "Error: package name null"); + return; + } + if (SCROLL_BOOST_SS_ENABLE) { + if (mPerfBoostFling == null) { + mPerfBoostFling = new BoostFramework(); + mIsPerfBoostFlingAcquired = false; + } + if (mPerfBoostFling == null) { + Slog.e(TAG, "Error: boost object null"); + return; + } + boolean isGame = isTopAppGame(currentPackage, mPerfBoostFling); + if (!isGame) { + mPerfBoostFling.perfHint(BoostFramework.VENDOR_HINT_SCROLL_BOOST, + currentPackage, duration + 160, BoostFramework.Scroll.VERTICAL); + mIsPerfBoostFlingAcquired = true; + } + } + } + + @Override + public void onHorizontalFling(int duration) { + String currentPackage = getAppPackageName(); + if (currentPackage == null) { + Slog.e(TAG, "Error: package name null"); + return; + } + if (SCROLL_BOOST_SS_ENABLE) { + if (mPerfBoostFling == null) { + mPerfBoostFling = new BoostFramework(); + mIsPerfBoostFlingAcquired = false; + } + if (mPerfBoostFling == null) { + Slog.e(TAG, "Error: boost object null"); + return; + } + boolean isGame = isTopAppGame(currentPackage, mPerfBoostFling); + if (!isGame) { + mPerfBoostFling.perfHint(BoostFramework.VENDOR_HINT_SCROLL_BOOST, + currentPackage, duration + 160, BoostFramework.Scroll.HORIZONTAL); + mIsPerfBoostFlingAcquired = true; + } + } + } + + @Override + public void onScroll(boolean started) { + String currentPackage = getAppPackageName(); + if (currentPackage == null) { + Slog.e(TAG, "Error: package name null"); + return; + } + boolean isGame; + if (mPerfBoostDrag == null) { + mPerfBoostDrag = new BoostFramework(); + } + if (mPerfBoostDrag == null) { + Slog.e(TAG, "Error: boost object null"); + return; + } + if (SCROLL_BOOST_SS_ENABLE) { + if (mPerfBoostPrefling == null) { + mPerfBoostPrefling = new BoostFramework(); + } + if (mPerfBoostPrefling == null) { + Slog.e(TAG, "Error: boost object null"); + return; + } + isGame = isTopAppGame(currentPackage, mPerfBoostPrefling); + if (!isGame) { + mPerfBoostPrefling.perfHint(BoostFramework.VENDOR_HINT_SCROLL_BOOST, + currentPackage, -1, BoostFramework.Scroll.PREFILING); + } + } + isGame = isTopAppGame(currentPackage, mPerfBoostDrag); + if (!isGame && started) { + if (DRAG_PLH_ENABLE) { + mPerfBoostDrag.perfEvent(BoostFramework.VENDOR_HINT_DRAG_START, currentPackage); + } + mPerfBoostDrag.perfHint(BoostFramework.VENDOR_HINT_DRAG_BOOST, + currentPackage, -1, 1); + } else { + if (DRAG_PLH_ENABLE) { + mPerfBoostDrag.perfEvent(BoostFramework.VENDOR_HINT_DRAG_END, currentPackage); + } + mPerfBoostDrag.perfLockRelease(); + } + } + @Override public void onDebug() { // no-op @@ -541,6 +692,11 @@ public void onDown() { if (listener != null) { listener.onTouchStart(); } + if(SCROLL_BOOST_SS_ENABLE && mPerfBoostFling!= null + && mIsPerfBoostFlingAcquired) { + mPerfBoostFling.perfLockRelease(); + mIsPerfBoostFlingAcquired = false; + } } @Override diff --git a/services/core/java/com/android/server/wm/DisplayWindowSettings.java b/services/core/java/com/android/server/wm/DisplayWindowSettings.java index c1b6496338a4..87639001b4e8 100644 --- a/services/core/java/com/android/server/wm/DisplayWindowSettings.java +++ b/services/core/java/com/android/server/wm/DisplayWindowSettings.java @@ -442,11 +442,12 @@ boolean setTo(@NonNull SettingsEntry other) { mRemoveContentMode = other.mRemoveContentMode; changed = true; } - if (other.mShouldShowWithInsecureKeyguard != mShouldShowWithInsecureKeyguard) { + if (!Objects.equals( + other.mShouldShowWithInsecureKeyguard, mShouldShowWithInsecureKeyguard)) { mShouldShowWithInsecureKeyguard = other.mShouldShowWithInsecureKeyguard; changed = true; } - if (other.mShouldShowSystemDecors != mShouldShowSystemDecors) { + if (!Objects.equals(other.mShouldShowSystemDecors, mShouldShowSystemDecors)) { mShouldShowSystemDecors = other.mShouldShowSystemDecors; changed = true; } @@ -458,15 +459,15 @@ boolean setTo(@NonNull SettingsEntry other) { mFixedToUserRotation = other.mFixedToUserRotation; changed = true; } - if (other.mIgnoreOrientationRequest != mIgnoreOrientationRequest) { + if (!Objects.equals(other.mIgnoreOrientationRequest, mIgnoreOrientationRequest)) { mIgnoreOrientationRequest = other.mIgnoreOrientationRequest; changed = true; } - if (other.mIgnoreDisplayCutout != mIgnoreDisplayCutout) { + if (!Objects.equals(other.mIgnoreDisplayCutout, mIgnoreDisplayCutout)) { mIgnoreDisplayCutout = other.mIgnoreDisplayCutout; changed = true; } - if (other.mDontMoveToTop != mDontMoveToTop) { + if (!Objects.equals(other.mDontMoveToTop, mDontMoveToTop)) { mDontMoveToTop = other.mDontMoveToTop; changed = true; } @@ -522,14 +523,13 @@ boolean updateFrom(@NonNull SettingsEntry delta) { mRemoveContentMode = delta.mRemoveContentMode; changed = true; } - if (delta.mShouldShowWithInsecureKeyguard != null - && delta.mShouldShowWithInsecureKeyguard - != mShouldShowWithInsecureKeyguard) { + if (delta.mShouldShowWithInsecureKeyguard != null && !Objects.equals( + delta.mShouldShowWithInsecureKeyguard, mShouldShowWithInsecureKeyguard)) { mShouldShowWithInsecureKeyguard = delta.mShouldShowWithInsecureKeyguard; changed = true; } - if (delta.mShouldShowSystemDecors != null - && delta.mShouldShowSystemDecors != mShouldShowSystemDecors) { + if (delta.mShouldShowSystemDecors != null && !Objects.equals( + delta.mShouldShowSystemDecors, mShouldShowSystemDecors)) { mShouldShowSystemDecors = delta.mShouldShowSystemDecors; changed = true; } @@ -543,18 +543,18 @@ boolean updateFrom(@NonNull SettingsEntry delta) { mFixedToUserRotation = delta.mFixedToUserRotation; changed = true; } - if (delta.mIgnoreOrientationRequest != null - && delta.mIgnoreOrientationRequest != mIgnoreOrientationRequest) { + if (delta.mIgnoreOrientationRequest != null && !Objects.equals( + delta.mIgnoreOrientationRequest, mIgnoreOrientationRequest)) { mIgnoreOrientationRequest = delta.mIgnoreOrientationRequest; changed = true; } - if (delta.mIgnoreDisplayCutout != null - && delta.mIgnoreDisplayCutout != mIgnoreDisplayCutout) { + if (delta.mIgnoreDisplayCutout != null && !Objects.equals( + delta.mIgnoreDisplayCutout, mIgnoreDisplayCutout)) { mIgnoreDisplayCutout = delta.mIgnoreDisplayCutout; changed = true; } - if (delta.mDontMoveToTop != null - && delta.mDontMoveToTop != mDontMoveToTop) { + if (delta.mDontMoveToTop != null && !Objects.equals( + delta.mDontMoveToTop, mDontMoveToTop)) { mDontMoveToTop = delta.mDontMoveToTop; changed = true; } diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java index 4860762a5f7f..8d6a3d890dfb 100644 --- a/services/core/java/com/android/server/wm/RecentTasks.java +++ b/services/core/java/com/android/server/wm/RecentTasks.java @@ -67,6 +67,7 @@ import android.util.Slog; import android.util.SparseArray; import android.util.SparseBooleanArray; +import android.util.BoostFramework; import android.view.MotionEvent; import android.view.WindowManagerPolicyConstants.PointerEventListener; @@ -77,6 +78,8 @@ import com.google.android.collect.Sets; +import ink.kaleidoscope.server.ParallelSpaceManagerService; + import java.io.File; import java.io.PrintWriter; import java.util.ArrayList; @@ -201,6 +204,7 @@ interface Callbacks { private final HashMap mTmpAvailActCache = new HashMap<>(); private final HashMap mTmpAvailAppCache = new HashMap<>(); private final SparseBooleanArray mTmpQuietProfileUserIds = new SparseBooleanArray(); + private final BoostFramework mUxPerf = new BoostFramework(); // TODO(b/127498985): This is currently a rough heuristic for interaction inside an app private final PointerEventListener mListener = new PointerEventListener() { @@ -867,6 +871,7 @@ Set getProfileIds(int userId) { for (int i = 0; i < profileIds.length; i++) { userIds.add(Integer.valueOf(profileIds[i])); } + userIds.addAll(ParallelSpaceManagerService.getCurrentParallelUserIds()); return userIds; } @@ -976,7 +981,7 @@ private ArrayList getRecentTasksImpl(int maxNum, continue; } - res.add(createRecentTaskInfo(task, true /* stripExtras */)); + res.add(createRecentTaskInfo(task, true /* stripExtras */, getTasksAllowed)); } return res; } @@ -1204,6 +1209,22 @@ boolean addToBottom(Task task) { void remove(Task task) { mTasks.remove(task); notifyTaskRemoved(task, false /* wasTrimmed */, false /* killProcess */); + if (task != null) { + final Intent intent = task.getBaseIntent(); + if (intent == null) return; + final ComponentName componentName = intent.getComponent(); + if (componentName == null) return; + + final String taskPkgName = componentName.getPackageName(); + if (mUxPerf != null) { + if (mUxPerf.board_first_api_lvl < BoostFramework.VENDOR_T_API_LEVEL && + mUxPerf.board_api_lvl < BoostFramework.VENDOR_T_API_LEVEL) { + mUxPerf.perfUXEngine_events(BoostFramework.UXE_EVENT_KILL, 0, taskPkgName, 0); + } else { + mUxPerf.perfEvent(BoostFramework.VENDOR_HINT_KILL, taskPkgName, 2, 0, 0); + } + } + } } /** @@ -1895,7 +1916,8 @@ void dump(PrintWriter pw, boolean dumpAll, String dumpPackage) { /** * Creates a new RecentTaskInfo from a Task. */ - ActivityManager.RecentTaskInfo createRecentTaskInfo(Task tr, boolean stripExtras) { + ActivityManager.RecentTaskInfo createRecentTaskInfo(Task tr, boolean stripExtras, + boolean getTasksAllowed) { final ActivityManager.RecentTaskInfo rti = new ActivityManager.RecentTaskInfo(); // If the recent Task is detached, we consider it will be re-attached to the default // TaskDisplayArea because we currently only support recent overview in the default TDA. @@ -1907,6 +1929,9 @@ ActivityManager.RecentTaskInfo createRecentTaskInfo(Task tr, boolean stripExtras rti.id = rti.isRunning ? rti.taskId : INVALID_TASK_ID; rti.persistentId = rti.taskId; rti.lastSnapshotData.set(tr.mLastTaskSnapshotData); + if (!getTasksAllowed) { + Task.trimIneffectiveInfo(tr, rti); + } // Fill in organized child task info for the task created by organizer. if (tr.mCreatedByOrganizer) { diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index 7f222423ec1c..ca0be88c8eb7 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -49,6 +49,7 @@ import static com.android.server.policy.PhoneWindowManager.SYSTEM_DIALOG_REASON_ASSIST; import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT; import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; +import static com.android.server.wm.ActivityRecord.State.DESTROYED; import static com.android.server.wm.ActivityRecord.State.FINISHING; import static com.android.server.wm.ActivityRecord.State.PAUSED; import static com.android.server.wm.ActivityRecord.State.RESUMED; @@ -127,6 +128,7 @@ import android.service.voice.IVoiceInteractionSession; import android.util.ArrayMap; import android.util.ArraySet; +import android.util.BoostFramework; import android.util.IntArray; import android.util.Pair; import android.util.Slog; @@ -166,7 +168,7 @@ import java.util.function.Predicate; /** Root {@link WindowContainer} for the device. */ -class RootWindowContainer extends WindowContainer +public class RootWindowContainer extends WindowContainer implements DisplayManager.DisplayListener { private static final String TAG = TAG_WITH_CLASS_NAME ? "RootWindowContainer" : TAG_WM; @@ -237,6 +239,12 @@ class RootWindowContainer extends WindowContainer DisplayManager mDisplayManager; private DisplayManagerInternal mDisplayManagerInternal; + public static boolean mPerfSendTapHint = false; + public static boolean mIsPerfBoostAcquired = false; + public static int mPerfHandle = -1; + public BoostFramework mPerfBoost = null; + public BoostFramework mUxPerf = null; + /** Reference to default display so we can quickly look it up. */ private DisplayContent mDefaultDisplay; private final SparseArray mDisplayAccessUIDs = new SparseArray<>(); @@ -1780,7 +1788,7 @@ List getTopVisibleActivities() { } @Nullable - Task getTopDisplayFocusedRootTask() { + public Task getTopDisplayFocusedRootTask() { for (int i = getChildCount() - 1; i >= 0; --i) { final Task focusedRootTask = getChildAt(i).getFocusedRootTask(); if (focusedRootTask != null) { @@ -2169,15 +2177,95 @@ void executeAppTransitionForAllDisplay() { } } + void acquireAppLaunchPerfLock(ActivityRecord r) { + /* Acquire perf lock during new app launch */ + if (mPerfBoost == null) { + mPerfBoost = new BoostFramework(); + } + if (mPerfBoost != null) { + int pkgType = mPerfBoost.perfGetFeedback(BoostFramework.VENDOR_FEEDBACK_WORKLOAD_TYPE, + r.packageName); + int wpcPid = -1; + if (mService != null && r != null && r.info != null && r.info.applicationInfo !=null) { + final WindowProcessController wpc = + mService.getProcessController(r.processName, r.info.applicationInfo.uid); + if (wpc != null && wpc.hasThread()) { + //If target process didn't start yet, + // this operation will be done when app call attach + wpcPid = wpc.getPid(); + } + } + if (mPerfBoost.getPerfHalVersion() >= BoostFramework.PERF_HAL_V23) { + mPerfBoost.perfHintAcqRel(-1, BoostFramework.VENDOR_HINT_FIRST_LAUNCH_BOOST, + r.packageName, -1, BoostFramework.Launch.BOOST_V1, 2, pkgType, wpcPid); + mPerfSendTapHint = true; + mPerfBoost.perfHintAcqRel(-1, BoostFramework.VENDOR_HINT_FIRST_LAUNCH_BOOST, + r.packageName, -1, BoostFramework.Launch.BOOST_V2, 2, pkgType, wpcPid); + if (wpcPid != -1) { + mPerfBoost.perfHintAcqRel(-1, + BoostFramework.VENDOR_HINT_FIRST_LAUNCH_BOOST, + r.packageName, wpcPid, + BoostFramework.Launch.TYPE_ATTACH_APPLICATION, 2, pkgType, wpcPid); + } + + if (pkgType == BoostFramework.WorkloadType.GAME) + { + mPerfHandle = mPerfBoost.perfHintAcqRel(-1, + BoostFramework.VENDOR_HINT_FIRST_LAUNCH_BOOST, + r.packageName, -1, BoostFramework.Launch.BOOST_GAME, 2, pkgType, wpcPid); + } else { + mPerfHandle = mPerfBoost.perfHintAcqRel(-1, + BoostFramework.VENDOR_HINT_FIRST_LAUNCH_BOOST, + r.packageName, -1, BoostFramework.Launch.BOOST_V3, 2, pkgType, wpcPid); + } + } else { + mPerfBoost.perfHint(BoostFramework.VENDOR_HINT_FIRST_LAUNCH_BOOST, r.packageName, + -1, BoostFramework.Launch.BOOST_V1); + mPerfSendTapHint = true; + mPerfBoost.perfHint(BoostFramework.VENDOR_HINT_FIRST_LAUNCH_BOOST, + r.packageName, -1, BoostFramework.Launch.BOOST_V2); + if (wpcPid != -1) { + mPerfBoost.perfHint( + BoostFramework.VENDOR_HINT_FIRST_LAUNCH_BOOST, + r.packageName, wpcPid, + BoostFramework.Launch.TYPE_ATTACH_APPLICATION); + } + + if (pkgType == BoostFramework.WorkloadType.GAME) + { + mPerfHandle = mPerfBoost.perfHint( + BoostFramework.VENDOR_HINT_FIRST_LAUNCH_BOOST, + r.packageName, -1, BoostFramework.Launch.BOOST_GAME); + } else { + mPerfHandle = mPerfBoost.perfHint( + BoostFramework.VENDOR_HINT_FIRST_LAUNCH_BOOST, + r.packageName, -1, BoostFramework.Launch.BOOST_V3); + } + } + if (mPerfHandle > 0) + mIsPerfBoostAcquired = true; + // Start IOP + if(r.info.applicationInfo != null && + r.info.applicationInfo.sourceDir != null) { + if (mPerfBoost.board_first_api_lvl < BoostFramework.VENDOR_T_API_LEVEL && + mPerfBoost.board_api_lvl < BoostFramework.VENDOR_T_API_LEVEL) { + mPerfBoost.perfIOPrefetchStart(-1,r.packageName, + r.info.applicationInfo.sourceDir.substring( + 0, r.info.applicationInfo.sourceDir.lastIndexOf('/'))); + } + } + } + } + @Nullable ActivityRecord findTask(ActivityRecord r, TaskDisplayArea preferredTaskDisplayArea) { return findTask(r.getActivityType(), r.taskAffinity, r.intent, r.info, - preferredTaskDisplayArea); + preferredTaskDisplayArea, r); } @Nullable ActivityRecord findTask(int activityType, String taskAffinity, Intent intent, ActivityInfo info, - TaskDisplayArea preferredTaskDisplayArea) { + TaskDisplayArea preferredTaskDisplayArea, ActivityRecord r) { ProtoLog.d(WM_DEBUG_TASKS, "Looking for task of type=%s, taskAffinity=%s, intent=%s" + ", info=%s, preferredTDA=%s", activityType, taskAffinity, intent, info, preferredTaskDisplayArea); @@ -2188,12 +2276,35 @@ ActivityRecord findTask(int activityType, String taskAffinity, Intent intent, Ac if (preferredTaskDisplayArea != null) { mTmpFindTaskResult.process(preferredTaskDisplayArea); if (mTmpFindTaskResult.mIdealRecord != null) { + if(mTmpFindTaskResult.mIdealRecord.getState() == DESTROYED) { + /*It's a new app launch */ + acquireAppLaunchPerfLock(r); + } + + if(mTmpFindTaskResult.mIdealRecord.getState() == STOPPED) { + /*Warm launch */ + mUxPerf = new BoostFramework(); + if (mUxPerf != null) { + if (mUxPerf.board_first_api_lvl < BoostFramework.VENDOR_T_API_LEVEL && + mUxPerf.board_api_lvl < BoostFramework.VENDOR_T_API_LEVEL) { + mUxPerf.perfUXEngine_events(BoostFramework.UXE_EVENT_SUB_LAUNCH, 0, r.packageName, 0); + } else { + mUxPerf.perfEvent(BoostFramework.VENDOR_HINT_WARM_LAUNCH, r.packageName, 2, 0, 0); + } + } + } return mTmpFindTaskResult.mIdealRecord; } else if (mTmpFindTaskResult.mCandidateRecord != null) { candidateActivity = mTmpFindTaskResult.mCandidateRecord; } } + /* Acquire perf lock *only* during new app launch */ + if ((mTmpFindTaskResult.mIdealRecord == null) || + (mTmpFindTaskResult.mIdealRecord.getState() == DESTROYED)) { + acquireAppLaunchPerfLock(r); + } + final ActivityRecord idealMatchActivity = getItemFromTaskDisplayAreas(taskDisplayArea -> { if (taskDisplayArea == preferredTaskDisplayArea) { return null; diff --git a/services/core/java/com/android/server/wm/RunningTasks.java b/services/core/java/com/android/server/wm/RunningTasks.java index 120fec0fe0e6..0e60274ba381 100644 --- a/services/core/java/com/android/server/wm/RunningTasks.java +++ b/services/core/java/com/android/server/wm/RunningTasks.java @@ -142,6 +142,10 @@ private RunningTaskInfo createRunningTaskInfo(Task task) { task.fillTaskInfo(rti, !mKeepIntentExtra); // Fill in some deprecated values rti.id = rti.taskId; + + if (!mAllowed) { + Task.trimIneffectiveInfo(task, rti); + } return rti; } } diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java index fd8b614de9b7..268b0f931426 100644 --- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java +++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java @@ -42,6 +42,7 @@ import android.hardware.HardwareBuffer; import android.os.IBinder; import android.os.Trace; +import android.util.BoostFramework; import android.util.Slog; import android.util.proto.ProtoOutputStream; import android.view.DisplayAddress; @@ -92,6 +93,9 @@ class ScreenRotationAnimation { private static final String TAG = TAG_WITH_CLASS_NAME ? "ScreenRotationAnimation" : TAG_WM; + private BoostFramework mPerf = null; + private boolean mIsPerfLockAcquired = false; + private final Context mContext; private final DisplayContent mDisplayContent; private final float[] mTmpFloats = new float[9]; @@ -141,6 +145,8 @@ class ScreenRotationAnimation { final int width = currentBounds.width(); final int height = currentBounds.height(); + mPerf = new BoostFramework(); + // Screenshot does NOT include rotation! final DisplayInfo displayInfo = displayContent.getDisplayInfo(); final int realOriginalRotation = displayInfo.rotation; @@ -491,6 +497,10 @@ public boolean dismiss(SurfaceControl.Transaction t, long maxAnimationDuration, mDisplayContent.getWindowingLayer()); startAnimation(t, maxAnimationDuration, animationScale, finalWidth, finalHeight, exitAnim, enterAnim); + if (mPerf != null && !mIsPerfLockAcquired) { + mPerf.perfHint(BoostFramework.VENDOR_HINT_ROTATION_ANIM_BOOST, null); + mIsPerfLockAcquired = true; + } } if (!mStarted) { return false; @@ -557,6 +567,11 @@ public void kill() { mRotateAlphaAnimation.cancel(); mRotateAlphaAnimation = null; } + + if (mPerf != null && mIsPerfLockAcquired) { + mPerf.perfLockRelease(); + mIsPerfLockAcquired = false; + } } public boolean isAnimating() { diff --git a/services/core/java/com/android/server/wm/SystemGesturesPointerEventListener.java b/services/core/java/com/android/server/wm/SystemGesturesPointerEventListener.java index 658f4efbdb2f..02d8c2a1413f 100644 --- a/services/core/java/com/android/server/wm/SystemGesturesPointerEventListener.java +++ b/services/core/java/com/android/server/wm/SystemGesturesPointerEventListener.java @@ -80,6 +80,7 @@ class SystemGesturesPointerEventListener implements PointerEventListener { private boolean mDebugFireable; private boolean mMouseHoveringAtEdge; private long mLastFlingTime; + private boolean mScrollFired; SystemGesturesPointerEventListener(Context context, Handler handler, Callbacks callbacks) { mContext = checkNull("context", context); @@ -172,6 +173,7 @@ public void onPointerEvent(MotionEvent event) { case MotionEvent.ACTION_DOWN: mSwipeFireable = true; mDebugFireable = true; + mScrollFired = false; mDownPointers = 0; captureDown(event, 0); if (mMouseHoveringAtEdge) { @@ -228,6 +230,9 @@ public void onPointerEvent(MotionEvent event) { case MotionEvent.ACTION_CANCEL: mSwipeFireable = false; mDebugFireable = false; + if (mScrollFired) + mCallbacks.onScroll(false); + mScrollFired = false; mCallbacks.onUpOrCancel(); break; default: @@ -359,10 +364,25 @@ public boolean onFling(MotionEvent down, MotionEvent up, if (duration > MAX_FLING_TIME_MILLIS) { duration = MAX_FLING_TIME_MILLIS; } + if(Math.abs(velocityY) >= Math.abs(velocityX)) + mCallbacks.onVerticalFling(duration); + else + mCallbacks.onHorizontalFling(duration); + mLastFlingTime = now; mCallbacks.onFling(duration); return true; } + + @Override + public boolean onScroll(MotionEvent e1, MotionEvent e2, + float distanceX, float distanceY) { + if (!mScrollFired) { + mCallbacks.onScroll(true); + mScrollFired = true; + } + return true; + } } interface Callbacks { @@ -371,6 +391,9 @@ interface Callbacks { void onSwipeFromRight(); void onSwipeFromLeft(); void onFling(int durationMs); + void onVerticalFling(int durationMs); + void onHorizontalFling(int durationMs); + void onScroll(boolean started); void onDown(); void onUpOrCancel(); void onMouseHoverAtTop(); diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index aede32c453f2..23ace0cdda75 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -38,6 +38,7 @@ import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME; import static android.content.pm.ActivityInfo.CONFIG_SCREEN_LAYOUT; import static android.content.pm.ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY; +import static android.content.pm.ActivityInfo.FLAG_RESUME_WHILE_PAUSING; import static android.content.pm.ActivityInfo.FLAG_SHOW_FOR_ALL_USERS; import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_LANDSCAPE_ONLY; import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PORTRAIT_ONLY; @@ -47,6 +48,7 @@ import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_AND_PIPABLE_DEPRECATED; import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET; + import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; import static android.provider.Settings.Secure.USER_SETUP_COMPLETE; import static android.view.Display.DEFAULT_DISPLAY; @@ -144,6 +146,7 @@ import android.app.PictureInPictureParams; import android.app.TaskInfo; import android.app.WindowConfiguration; +import android.app.servertransaction.PauseActivityItem; import android.content.ComponentName; import android.content.Intent; import android.content.pm.ActivityInfo; @@ -169,6 +172,7 @@ import android.provider.Settings; import android.service.voice.IVoiceInteractionSession; import android.util.ArraySet; +import android.util.BoostFramework; import android.util.DisplayMetrics; import android.util.Slog; import android.util.TypedXmlPullParser; @@ -503,6 +507,9 @@ class Task extends TaskFragment { private final Handler mHandler; + private static final ActivityPluginDelegate mActivityPluginDelegate = + new ActivityPluginDelegate(); + private class ActivityTaskHandler extends Handler { ActivityTaskHandler(Looper looper) { @@ -3446,6 +3453,27 @@ void fillTaskInfo(TaskInfo info, boolean stripExtras, @Nullable TaskDisplayArea info.isSleeping = shouldSleepActivities(); } + /** + * Removes the activity info if the activity belongs to a different uid, which is + * different from the app that hosts the task. + */ + static void trimIneffectiveInfo(Task task, TaskInfo info) { + final ActivityRecord baseActivity = task.getActivity(r -> !r.finishing, + false /* traverseTopToBottom */); + final int baseActivityUid = + baseActivity != null ? baseActivity.getUid() : task.effectiveUid; + + if (info.topActivityInfo != null + && task.effectiveUid != info.topActivityInfo.applicationInfo.uid) { + info.topActivity = null; + info.topActivityInfo = null; + } + + if (task.effectiveUid != baseActivityUid) { + info.baseActivity = null; + } + } + @Nullable PictureInPictureParams getPictureInPictureParams() { final Task topTask = getTopMostTask(); if (topTask == null) return null; @@ -4924,13 +4952,11 @@ private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOption // Not ready yet! return false; } - final ActivityRecord topActivity = topRunningActivity(true /* focusableOnly */); if (topActivity == null) { // There are no activities left in this task, let's look somewhere else. return resumeNextFocusableActivityWhenRootTaskIsEmpty(prev, options); } - final boolean[] resumed = new boolean[1]; final TaskFragment topFragment = topActivity.getTaskFragment(); resumed[0] = topFragment.resumeTopActivity(prev, options, deferPause); @@ -5012,6 +5038,11 @@ void startActivityLocked(ActivityRecord r, @Nullable Task topTask, boolean newTa ProtoLog.i(WM_DEBUG_ADD_REMOVE, "Adding activity %s to task %s " + "callers: %s", r, task, new RuntimeException("here").fillInStackTrace()); + if (mActivityPluginDelegate != null) { + mActivityPluginDelegate.activityInvokeNotification + (r.info.packageName, getWindowingMode() == WINDOWING_MODE_FULLSCREEN); + } + // The transition animation and starting window are not needed if {@code allowMoveToFront} // is false, because the activity won't be visible. if ((!isActivityTypeHomeOrRecents() || hasActivity()) && allowMoveToFront) { @@ -6077,6 +6108,13 @@ AnimatingActivityRegistry getAnimatingActivityRegistry() { return mAnimatingActivityRegistry; } + public void onARStopTriggered(ActivityRecord r) { + if (mActivityPluginDelegate != null && getWindowingMode() != WINDOWING_MODE_UNDEFINED) { + mActivityPluginDelegate.activitySuspendNotification + (r.info.applicationInfo.packageName, getWindowingMode() == WINDOWING_MODE_FULLSCREEN, false); + } + } + @Override void executeAppTransition(ActivityOptions options) { mDisplayContent.executeAppTransition(); diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java index 058a066fffa4..7993e029a1e6 100644 --- a/services/core/java/com/android/server/wm/TaskFragment.java +++ b/services/core/java/com/android/server/wm/TaskFragment.java @@ -82,6 +82,7 @@ import android.os.IBinder; import android.os.RemoteException; import android.os.UserHandle; +import android.util.BoostFramework; import android.util.DisplayMetrics; import android.util.Slog; import android.util.proto.ProtoOutputStream; @@ -93,6 +94,7 @@ import android.window.TaskFragmentOrganizerToken; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.app.ActivityTrigger; import com.android.internal.protolog.common.ProtoLog; import com.android.internal.util.function.pooled.PooledLambda; import com.android.internal.util.function.pooled.PooledPredicate; @@ -199,6 +201,10 @@ class TaskFragment extends WindowContainer { final RootWindowContainer mRootWindowContainer; private final TaskFragmentOrganizerController mTaskFragmentOrganizerController; + public BoostFramework mPerf = null; + //ActivityTrigger + static final ActivityTrigger mActivityTrigger = new ActivityTrigger(); + // TODO(b/233177466): Move mMinWidth and mMinHeight to Task and remove usages in TaskFragment /** * Minimal width of this task fragment when it's resizeable. {@link #INVALID_MIN_SIZE} means it @@ -1198,6 +1204,13 @@ final boolean resumeTopActivity(ActivityRecord prev, ActivityOptions options, if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Resuming " + next); + //Trigger Activity Resume + if (mActivityTrigger != null) { + mActivityTrigger.activityResumeTrigger(next.intent, next.info, + next.info.applicationInfo, + next.occludesParent()); + } + mTaskSupervisor.setLaunchSource(next.info.applicationInfo.uid); ActivityRecord lastResumed = null; @@ -1300,6 +1313,11 @@ final boolean resumeTopActivity(ActivityRecord prev, ActivityOptions options, // to ignore it when computing the desired screen orientation. boolean anim = true; final DisplayContent dc = taskDisplayArea.mDisplayContent; + + if (mPerf == null) { + mPerf = new BoostFramework(); + } + if (prev != null) { if (prev.finishing) { if (DEBUG_TRANSITION) { @@ -1309,6 +1327,10 @@ final boolean resumeTopActivity(ActivityRecord prev, ActivityOptions options, anim = false; dc.prepareAppTransition(TRANSIT_NONE); } else { + if(prev.getTask() != next.getTask() && mPerf != null) { + mPerf.perfHint(BoostFramework.VENDOR_HINT_ANIM_BOOST, + next.packageName); + } dc.prepareAppTransition(TRANSIT_CLOSE); } prev.setVisibility(false); @@ -1320,6 +1342,10 @@ final boolean resumeTopActivity(ActivityRecord prev, ActivityOptions options, anim = false; dc.prepareAppTransition(TRANSIT_NONE); } else { + if(prev.getTask() != next.getTask() && mPerf != null) { + mPerf.perfHint(BoostFramework.VENDOR_HINT_ANIM_BOOST, + next.packageName); + } dc.prepareAppTransition(TRANSIT_OPEN, next.mLaunchTaskBehind ? TRANSIT_FLAG_OPEN_BEHIND : 0); } @@ -1583,6 +1609,12 @@ boolean startPausing(boolean userLeaving, boolean uiSleeping, ActivityRecord res return false; } + //Trigger Activity Pause + if (mActivityTrigger != null) { + mActivityTrigger.activityPauseTrigger(prev.intent, prev.info, + prev.info.applicationInfo); + } + ProtoLog.v(WM_DEBUG_STATES, "Moving to PAUSING: %s", prev); mPausingActivity = prev; mLastPausedActivity = prev; diff --git a/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java b/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java index 523b484269c0..86b873d4a546 100644 --- a/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java +++ b/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java @@ -30,6 +30,10 @@ import android.view.WindowManagerPolicyConstants.PointerEventListener; import com.android.server.wm.WindowManagerService.H; +import com.android.server.am.ActivityManagerService; +import com.android.server.wm.ActivityTaskSupervisor; +import com.android.server.wm.DisplayContent; +import android.util.BoostFramework; /** * 1. Adjust the top most focus display if touch down on some display. @@ -42,11 +46,15 @@ public class TaskTapPointerEventListener implements PointerEventListener { private final DisplayContent mDisplayContent; private final Rect mTmpRect = new Rect(); private int mPointerIconType = TYPE_NOT_SPECIFIED; + public BoostFramework mPerfObj = null; public TaskTapPointerEventListener(WindowManagerService service, DisplayContent displayContent) { mService = service; mDisplayContent = displayContent; + if (mPerfObj == null) { + mPerfObj = new BoostFramework(); + } } private void restorePointerIcon(int x, int y) { @@ -129,6 +137,29 @@ public void onPointerEvent(MotionEvent motionEvent) { } break; } + if (ActivityTaskSupervisor.mIsPerfBoostAcquired && (mPerfObj != null)) { + if (ActivityTaskSupervisor.mPerfHandle > 0) { + mPerfObj.perfLockReleaseHandler(ActivityTaskSupervisor.mPerfHandle); + ActivityTaskSupervisor.mPerfHandle = -1; + } + ActivityTaskSupervisor.mIsPerfBoostAcquired = false; + } + if (ActivityTaskSupervisor.mPerfSendTapHint && (mPerfObj != null)) { + mPerfObj.perfHint(BoostFramework.VENDOR_HINT_TAP_EVENT, null); + ActivityTaskSupervisor.mPerfSendTapHint = false; + } + if (RootWindowContainer.mIsPerfBoostAcquired && (mPerfObj != null)) { + if (RootWindowContainer.mPerfHandle > 0) { + mPerfObj.perfLockReleaseHandler( + RootWindowContainer.mPerfHandle); + RootWindowContainer.mPerfHandle = -1; + } + RootWindowContainer.mIsPerfBoostAcquired = false; + } + if (RootWindowContainer.mPerfSendTapHint && (mPerfObj != null)) { + mPerfObj.perfHint(BoostFramework.VENDOR_HINT_TAP_EVENT, null); + RootWindowContainer.mPerfSendTapHint = false; + } } void setTouchExcludeRegion(Region newRegion) { diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 4d37e0816639..31be69e49d2d 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -229,6 +229,7 @@ import android.text.format.DateUtils; import android.util.ArrayMap; import android.util.ArraySet; +import android.util.BoostFramework; import android.util.DisplayMetrics; import android.util.EventLog; import android.util.MergedConfiguration; @@ -324,6 +325,7 @@ import com.android.server.utils.PriorityDump; import dalvik.annotation.optimization.NeverCompile; +import ink.kaleidoscope.server.ParallelSpaceManagerService; import java.io.BufferedWriter; import java.io.DataInputStream; @@ -361,6 +363,8 @@ public class WindowManagerService extends IWindowManager.Stub static final int LAYOUT_REPEAT_THRESHOLD = 4; static final boolean PROFILE_ORIENTATION = false; + static WindowState mFocusingWindow; + String mFocusingActivity; /** The maximum length we will accept for a loaded animation duration: * this is 10 seconds. @@ -471,6 +475,8 @@ public class WindowManagerService extends IWindowManager.Stub private final DisplayAreaPolicy.Provider mDisplayAreaPolicyProvider; + private BoostFramework mPerf = null; + final private KeyguardDisableHandler mKeyguardDisableHandler; private final RemoteCallbackList mKeyguardLockedStateListeners = @@ -798,8 +804,14 @@ final class SettingsObserver extends ContentObserver { public SettingsObserver() { super(new Handler()); ContentResolver resolver = mContext.getContentResolver(); - resolver.registerContentObserver(mDisplayInversionEnabledUri, false, this, - UserHandle.USER_ALL); + + final boolean displayInversionAvailable = mContext.getResources().getBoolean( + com.android.internal.R.bool.config_displayInversionAvailable); + if (displayInversionAvailable) { + resolver.registerContentObserver(mDisplayInversionEnabledUri, false, this, + UserHandle.USER_ALL); + } + resolver.registerContentObserver(mWindowAnimationScaleUri, false, this, UserHandle.USER_ALL); resolver.registerContentObserver(mTransitionAnimationScaleUri, false, this, @@ -3481,12 +3493,28 @@ private void setAnimatorDurationScale(float scale) { ValueAnimator.setDurationScale(scale); } + private float animationScalesCheck (int which) { + float value = -1.0f; + if (!mAnimationsDisabled) { + if (value == -1.0f) { + switch (which) { + case WINDOW_ANIMATION_SCALE: value = mWindowAnimationScaleSetting; break; + case TRANSITION_ANIMATION_SCALE: value = mTransitionAnimationScaleSetting; break; + case ANIMATION_DURATION_SCALE: value = mAnimatorDurationScaleSetting; break; + } + } + } else { + value = 0; + } + return value; + } + public float getWindowAnimationScaleLocked() { - return mAnimationsDisabled ? 0 : mWindowAnimationScaleSetting; + return animationScalesCheck(WINDOW_ANIMATION_SCALE); } public float getTransitionAnimationScaleLocked() { - return mAnimationsDisabled ? 0 : mTransitionAnimationScaleSetting; + return animationScalesCheck(TRANSITION_ANIMATION_SCALE); } @Override @@ -3606,6 +3634,18 @@ public void rebootSafeMode(boolean confirm) { confirm); } + // Called by window manager policy. Not exposed externally. + @Override + public void reboot(String reason, boolean confirm) { + ShutdownThread.reboot(ActivityThread.currentActivityThread().getSystemUiContext(), reason, confirm); + } + + // Called by window manager policy. Not exposed externally. + @Override + public void rebootCustom(String reason, boolean confirm) { + ShutdownThread.rebootCustom(ActivityThread.currentActivityThread().getSystemUiContext(), reason, confirm); + } + public void setCurrentProfileIds(final int[] currentProfileIds) { synchronized (mGlobalLock) { mCurrentProfileIds = currentProfileIds; @@ -3643,7 +3683,7 @@ boolean isCurrentProfile(int userId) { for (int i = 0; i < mCurrentProfileIds.length; i++) { if (mCurrentProfileIds[i] == userId) return true; } - return false; + return ParallelSpaceManagerService.isCurrentParallelUser(userId); } public void enableScreenAfterBoot() { @@ -6121,6 +6161,12 @@ private void doStartFreezingDisplay(int exitAnim, int enterAnim, DisplayContent } mLatencyTracker.onActionStart(ACTION_ROTATE_SCREEN); + if (mPerf == null) { + mPerf = new BoostFramework(); + } + if (mPerf != null) { + mPerf.perfHint(BoostFramework.VENDOR_HINT_ROTATION_LATENCY_BOOST, null); + } mExitAnimId = exitAnim; mEnterAnimId = enterAnim; @@ -6251,6 +6297,9 @@ private void doStopFreezingDisplayLocked(DisplayContent displayContent) { } mAtmService.endLaunchPowerMode(POWER_MODE_REASON_CHANGE_DISPLAY); mLatencyTracker.onActionEnd(ACTION_ROTATE_SCREEN); + if (mPerf != null) { + mPerf.perfLockRelease(); + } } static int getPropertyInt(String[] tokens, int index, int defUnits, int defDps, diff --git a/services/core/java/com/android/server/wm/WindowManagerThreadPriorityBooster.java b/services/core/java/com/android/server/wm/WindowManagerThreadPriorityBooster.java index 6f2930c46b12..1b70d1d4a8b6 100644 --- a/services/core/java/com/android/server/wm/WindowManagerThreadPriorityBooster.java +++ b/services/core/java/com/android/server/wm/WindowManagerThreadPriorityBooster.java @@ -21,7 +21,7 @@ import static android.os.Process.myTid; import static android.os.Process.setThreadPriority; -import static com.android.server.LockGuard.INDEX_WINDOW;; +import static com.android.server.LockGuard.INDEX_WINDOW; import com.android.internal.annotations.GuardedBy; import com.android.server.AnimationThread; diff --git a/services/core/java/ink/kaleidoscope/server/GmsManagerService.java b/services/core/java/ink/kaleidoscope/server/GmsManagerService.java new file mode 100644 index 000000000000..00a286baf4ef --- /dev/null +++ b/services/core/java/ink/kaleidoscope/server/GmsManagerService.java @@ -0,0 +1,238 @@ +/* + * Copyright (C) 2022 Project Kaleidoscope + * + * 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 ink.kaleidoscope.server; + +import static android.os.Process.THREAD_PRIORITY_DEFAULT; +import static android.provider.Settings.Secure.GMS_ENABLED; + +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.content.pm.IPackageManager; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.ParceledListSlice; +import android.content.pm.UserInfo; +import android.database.ContentObserver; +import android.net.Uri; +import android.os.Handler; +import android.os.IUserManager; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.provider.Settings; + +import com.android.server.ServiceThread; +import com.android.server.SystemService; + +import java.lang.Boolean; +import java.lang.IllegalArgumentException; +import java.lang.Integer; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; + +public final class GmsManagerService extends SystemService { + + static final String[] GMS_PACKAGES = + { + "com.android.vending", + "com.google.android.gms", + "com.google.android.gms.policy_sidecar_aps", + "com.google.android.gsf", + "com.google.android.projection.gearhead", + "com.google.android.syncadapters.calendar", + "com.google.android.syncadapters.contacts", + "com.google.android.apps.wellbeing", + "com.google.android.syncadapters.contacts", + "com.google.android.soundpicker", + "com.google.android.settings.intelligence" + }; + + private static final String TAG = "GmsManagerService"; + + private static HashMap sCachedSettings = new HashMap<>(); + + private final Context mContext; + private final IPackageManager mPM; + private final IUserManager mUM; + private final ContentResolver mResolver; + private final String mOpPackageName; + + private ServiceThread mWorker; + private Handler mHandler; + private HashMap mObservers; + + public static boolean shouldHide(int userId, String packageName) { + if (packageName == null) + return false; + + Boolean enabled = sCachedSettings.get(userId); + if (enabled == null) + return false; + + return !enabled.booleanValue() && + Arrays.stream(GMS_PACKAGES).anyMatch(packageName::equals); + } + + public static ParceledListSlice recreatePackageList( + int userId, ParceledListSlice list) { + Boolean enabled = sCachedSettings.get(userId); + if (enabled == null || enabled.booleanValue()) + return list; + + List oldList = list.getList(); + ArrayList newList = new ArrayList<>(); + for (PackageInfo info : oldList) { + if (info.packageName != null && + Arrays.stream(GMS_PACKAGES).anyMatch(info.packageName::equals)) + continue; + newList.add(info); + } + + return new ParceledListSlice<>(newList); + } + + public static List recreateApplicationList( + int userId, List list) { + Boolean enabled = sCachedSettings.get(userId); + if (enabled == null || enabled.booleanValue()) + return list; + + ArrayList newList = new ArrayList<>(); + for (ApplicationInfo info : list) { + if (info.packageName != null && + Arrays.stream(GMS_PACKAGES).anyMatch(info.packageName::equals)) + continue; + newList.add(info); + } + + return newList; + } + + private void updateStateForUser(int userId) { + boolean enabled = Settings.Secure.getIntForUser(mResolver, GMS_ENABLED, 1, userId) == 1; + sCachedSettings.put(userId, enabled); + + try { + for (String packageName : GMS_PACKAGES) { + try { + if (enabled) { + mPM.setApplicationEnabledSetting(packageName, + PackageManager.COMPONENT_ENABLED_STATE_DEFAULT, + 0, userId, mOpPackageName); + } else { + mPM.setApplicationEnabledSetting(packageName, + PackageManager.COMPONENT_ENABLED_STATE_DISABLED, + 0, userId, mOpPackageName); + } + } catch (IllegalArgumentException ignored) {} + } + } catch (RemoteException e) { + e.rethrowAsRuntimeException(); + } + } + + private void initForUser(int userId) { + if (userId < 0) + return; + + SettingsObserver observer = new SettingsObserver(mHandler, userId); + mResolver.registerContentObserver( + Settings.Secure.getUriFor(GMS_ENABLED), false, observer, userId); + mObservers.put(userId, observer); + + updateStateForUser(userId); + } + + private void deInitForUser(int userId) { + if (userId < 0) + return; + + SettingsObserver observer = mObservers.get(userId); + if (observer == null) + return; + + mResolver.unregisterContentObserver(observer); + mObservers.remove(userId); + sCachedSettings.remove(userId); + } + + private void init() { + try { + for (UserInfo user : mUM.getUsers(false, false, false)) { + initForUser(user.id); + } + } catch (RemoteException e) { + e.rethrowAsRuntimeException(); + } + + IntentFilter filter = new IntentFilter(); + filter.addAction(Intent.ACTION_USER_ADDED); + filter.addAction(Intent.ACTION_USER_REMOVED); + mContext.registerReceiver(new UserReceiver(), filter, + android.Manifest.permission.MANAGE_USERS, mHandler); + } + + @Override + public void onStart() { + mWorker = new ServiceThread(TAG, THREAD_PRIORITY_DEFAULT, false); + mWorker.start(); + mHandler = new Handler(mWorker.getLooper()); + + init(); + } + + public GmsManagerService(Context context) { + super(context); + mContext = context; + mResolver = context.getContentResolver(); + mPM = IPackageManager.Stub.asInterface(ServiceManager.getService("package")); + mUM = IUserManager.Stub.asInterface(ServiceManager.getService(Context.USER_SERVICE)); + mOpPackageName = context.getOpPackageName(); + mObservers = new HashMap<>(); + } + + private final class UserReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1); + + if (Intent.ACTION_USER_ADDED.equals(intent.getAction())) + initForUser(userId); + else + deInitForUser(userId); + } + } + + private final class SettingsObserver extends ContentObserver { + private int mUserId; + + public SettingsObserver(Handler handler, int userId) { + super(handler); + mUserId = userId; + } + + @Override + public void onChange(boolean selfChange, Uri uri) { + updateStateForUser(mUserId); + } + } +} diff --git a/services/core/java/ink/kaleidoscope/server/ParallelSpaceManagerService.java b/services/core/java/ink/kaleidoscope/server/ParallelSpaceManagerService.java new file mode 100644 index 000000000000..f02c49b2343c --- /dev/null +++ b/services/core/java/ink/kaleidoscope/server/ParallelSpaceManagerService.java @@ -0,0 +1,764 @@ +/* + * Copyright (C) 2022 Project Kaleidoscope + * + * 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 ink.kaleidoscope.server; + +import static android.os.Process.THREAD_PRIORITY_DEFAULT; +import static android.os.UserManager.USER_TYPE_PARALLEL_DEFAULT; +import static android.os.UserManager.USER_TYPE_PARALLEL_SHARE; +import static android.provider.Settings.Secure.USER_SETUP_COMPLETE; + +import static ink.kaleidoscope.ParallelSpaceManager.SERVICE_NAME; + +import android.app.ActivityManager; +import android.app.IActivityManager; +import android.content.BroadcastReceiver; +import android.content.ContentResolver; +import android.content.Context; +import android.content.ComponentName; +import android.content.IIntentReceiver; +import android.content.IIntentSender; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.IntentSender; +import android.content.pm.IPackageInstaller; +import android.content.pm.IPackageManager; +import android.content.pm.PackageInstaller; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.content.pm.UserInfo; +import android.content.pm.VersionedPackage; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.IUserManager; +import android.os.Message; +import android.os.Process; +import android.os.RemoteException; +import android.os.ResultReceiver; +import android.os.ServiceManager; +import android.os.ShellCallback; +import android.os.ShellCommand; +import android.os.UserHandle; +import android.provider.Settings; +import android.util.ArraySet; +import android.util.Slog; + +import com.android.internal.annotations.GuardedBy; +import com.android.server.ServiceThread; +import com.android.server.SystemService; + +import ink.kaleidoscope.IParallelSpaceManager; + +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.lang.Exception; +import java.lang.IllegalArgumentException; +import java.lang.InterruptedException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Set; + +/** + * All in one service for managing the lifecycle of parallel spaces. + */ +public final class ParallelSpaceManagerService extends SystemService { + + private static final String TAG = "ParallelSpaceManagerService"; + + /** + * By default, only non-launchable system apps will be initially installed in + * a new space. Here you can explicitly configure for this. + */ + private static final List SPACE_WHITELIST_PACKAGES = Arrays.asList( + // For granting permissions. + "com.android.settings", + // For managing files. + "com.android.documentsui", + "com.android.google.documentsui" + ); + + private static final List SPACE_BLOCKLIST_PACKAGES = Arrays.asList( + // To avoid third party apps starting it accidentally. + "com.android.fmradio", + "com.android.launcher3", + "com.caf.fmradio", + "com.google.android.apps.nexuslauncher", + "com.google.android.pixel.setupwizard", + "com.google.android.projection.gearhead", + "com.google.android.setupwizard" + ); + + /** + * Components should be disabled on space setup. + */ + private static final List SPACE_BLACKLIST_COMPONENTS = Arrays.asList( + // Remove settings icon from launcher. + "com.android.settings/com.android.settings.Settings" + ); + + private static final int MSG_UPDATE_PARALLEL_USER_LIST = 0; + private static final int MSG_START_SPACES = 1; + private static final int MSG_SETUP_SPACE = 2; + + private static final int RESTART_USER_DELAY = 3000; + + private Handler mHandler; + private Interface mInterface; + private IUserManager mUserManager; + private IActivityManager mActivityManager; + private PackageManager mPackageManager; + private IPackageManager mPackageManagerService; + private IPackageInstaller mPackageInstaller; + + /* + * Static variables that must be protected by mLock are below. + * It will be much cleaner to use LocalServices instead. But + * making it static is the quickest way to go through any + * sequential dependence problem as the callers are mostly + * early-start system services. + */ + private static Object mLock = new Object(); + @GuardedBy("mLock") + private static int mCurrentUserId; + @GuardedBy("mLock") + private static UserInfo mCurrentUser; + // Full parallel user information in case you are curious + @GuardedBy("mLock") + private static List mCurrentParallelUsers; + // Parallel user ids only for a quick search + @GuardedBy("mLock") + private static List mCurrentParallelUserIds; + // Profile users for interaction check + @GuardedBy("mLock") + private static List mCurrentProfileUsers; + @GuardedBy("mLock") + private static List mCurrentProfileUserIds; + // end of static variables + + /** + * Static methods that act as system server internal api. They can only be + * accessed within system server process. For the reason why we are using + * **static** methods, see the comments above static variables. + */ + + // Return parallel owner id if userId is a parallel user. + public static int convertToParallelOwnerIfPossible(int userId) { + synchronized (mLock) { + if (mCurrentParallelUserIds != null && mCurrentParallelUserIds.contains(userId)) + return mCurrentUserId; + return userId; + } + } + + // Check whether target user is a parallel user. + public static boolean isCurrentParallelUser(int userId) { + synchronized (mLock) { + if (mCurrentParallelUserIds != null) + return mCurrentParallelUserIds.contains(userId); + return false; + } + } + + // Get user id of currently foreground parallel space owner. + public static int getCurrentParallelOwnerId() { + synchronized (mLock) { + return mCurrentUserId; + } + } + + // Check whether target user is the parallel owner. + public static boolean isCurrentParallelOwner(int userId) { + return userId == getCurrentParallelOwnerId(); + } + + // Return a list of current parallel user ids. + public static List getCurrentParallelUserIds() { + // Must make a copy here because it can be dangerous to + // pass the reference. + synchronized (mLock) { + if (mCurrentParallelUserIds != null) + return new ArrayList(mCurrentParallelUserIds); + return Collections.emptyList(); + } + } + + // Whether a userId in in range of {owner, profiles, parallelSpaces}. + public static boolean isInteractive(int userId) { + synchronized (mLock) { + if (mCurrentParallelUserIds == null || mCurrentProfileUserIds == null) + return false; + + return userId == mCurrentUserId || mCurrentParallelUserIds.contains(userId) || + mCurrentProfileUserIds.contains(userId); + } + } + + // Owner user, profiles and parallel spaces should be able to + // interact with each other. + public static boolean canInteract(int userId1, int userId2) { + return isInteractive(userId1) && isInteractive(userId2); + } + + // Interactive users = owner + profiles + parallel spaces. + public static List getInteractiveUsers() { + ArrayList result = new ArrayList<>(); + synchronized (mLock) { + result.add(mCurrentUser); + result.addAll(mCurrentProfileUsers); + result.addAll(mCurrentParallelUsers); + } + return result; + } + + /* + * All internal methods should be either synchronized or be + * called on the service thread to avoid racing. + */ + private void handleMessageInternal(Message msg) { + switch (msg.what) { + case MSG_UPDATE_PARALLEL_USER_LIST: { + updateParallelUserListInternal(); + } break; + case MSG_START_SPACES: { + startSpacesIfNeededInternal(); + } break; + case MSG_SETUP_SPACE: { + setupSpaceIfNeededInternal(msg.arg1); + } break; + } + } + + private void setupSpaceIfNeededInternal(int userId) { + synchronized (mLock) { + if (mCurrentParallelUserIds == null || !mCurrentParallelUserIds.contains(userId)) + return; + } + + ContentResolver cr = getContext().getContentResolver(); + if (Settings.Secure.getIntForUser(cr, USER_SETUP_COMPLETE, 0, userId) == 1) + return; + + for (String name : SPACE_BLACKLIST_COMPONENTS) { + String[] splittedName = name.split("/"); + if (splittedName.length != 2) { + Slog.e(TAG, "Failed when resolving SPACE_BLACKLIST_COMPONENTS: " + name); + } + + ComponentName componentName = new ComponentName(splittedName[0], splittedName[1]); + try { + mPackageManagerService.setComponentEnabledSetting(componentName, + PackageManager.COMPONENT_ENABLED_STATE_DISABLED, + PackageManager.DONT_KILL_APP, userId); + } catch (RemoteException e) { + Slog.e(TAG, "Failed when disabling component: " + name); + } + } + + Settings.Secure.putIntForUser(cr, USER_SETUP_COMPLETE, 1, userId); + + // We are done. Notify the update. + broadcastChange(); + Slog.i(TAG, "User setup done for " + userId); + } + + private void startSpacesIfNeededInternal() { + List users; + synchronized (mLock) { + if (mCurrentParallelUserIds == null) + return; + users = new ArrayList<>(mCurrentParallelUserIds); + } + + try { + for (int userId : users) { + if (!mActivityManager.isUserRunning(userId, 0)) { + mActivityManager.startUserInBackground(userId); + Slog.i(TAG, "User started for " + userId); + } + } + } catch (RemoteException e) { + } + } + + private synchronized int removeSpaceInternal(int userId) { + synchronized (mLock) { + if (mCurrentParallelUserIds == null || + !mCurrentParallelUserIds.contains(userId)) { + // Fail fast. Although this shouldn't happen at all. + return -1; + } + } + + boolean success = false; + try { + success = mUserManager.removeUser(userId); + } catch (RemoteException e) { + } + + if (!success) { + Slog.e(TAG, "Failed when removing space: " + userId); + return -1; + } + + Slog.i(TAG, "Parallel space removed: " + userId); + // User removed. Update the list. + updateParallelUserListInternal(); + + return 0; + } + + private void killExternalStorageProvider() { + List victimUsers; + synchronized (mLock) { + victimUsers = new ArrayList<>(mCurrentParallelUserIds); + } + victimUsers.add(mCurrentUserId); + for (int userId : victimUsers) { + try { + mActivityManager.forceStopPackage("com.android.externalstorage", userId); + } catch (RemoteException e) { + Slog.e(TAG, "Failed when killing ExternalStorageProvider for user " + userId); + } + } + } + + private synchronized int createSpaceInternal(String name, boolean shareMedia) { + final int userId; + // Make a copy here because I don't want to call into + // other services with the global data lock held. + synchronized (mLock) { + userId = mCurrentUserId; + } + + String[] nonRequiredApps = getNonRequiredApps(userId).toArray(new String[0]); + UserInfo result = null; + + try { + if (shareMedia) { + result = mUserManager.createProfileForUserWithThrow( + name, USER_TYPE_PARALLEL_SHARE, 0, userId, nonRequiredApps); + } else { + result = mUserManager.createProfileForUserWithThrow( + name, USER_TYPE_PARALLEL_DEFAULT, 0, userId, nonRequiredApps); + } + } catch (RemoteException e) { + } + + if (result == null) { + Slog.e(TAG, "Failed when creating a new space"); + return -1; + } + + Slog.i(TAG, "New parallel space created: " + result.toFullString()); + // New user added. Update the list. + updateParallelUserListInternal(); + // List of users has been updated. Start them. + mHandler.removeMessages(MSG_START_SPACES); + mHandler.sendMessage(mHandler.obtainMessage(MSG_START_SPACES)); + + // HACK: For legacy devices running fuse above sdcardfs. + // Kill ExternalStorageProvider to make it restart with new gids. + killExternalStorageProvider(); + + return result.id; + } + + private synchronized void updateParallelUserListInternal() { + updateParallelUserListInternalNoBroadcast(); + // Notify interested clients. + broadcastChange(); + } + + private synchronized void updateParallelUserListInternalNoBroadcast() { + ArrayList parallelUsers = new ArrayList<>(); + ArrayList profileUsers = new ArrayList<>(); + ArrayList parallelUserIds = new ArrayList<>(); + ArrayList profileUserIds = new ArrayList<>(); + List users = null; + int userId = -1; + UserInfo curUser = null; + + try { + userId = mActivityManager.getCurrentUserId(); + users = mUserManager.getUsers(true, true, true); + } catch (RemoteException e) { + } + + if (userId < 0 || users == null) + return; + + for (UserInfo user : users) { + if (user.isParallel() && user.parallelParentId == userId) { + parallelUsers.add(user); + parallelUserIds.add(user.id); + continue; + } + if (user.isProfile() && user.profileGroupId == userId) { + profileUsers.add(user); + profileUserIds.add(user.id); + continue; + } + if (user.id == userId) { + curUser = user; + } + } + + synchronized (mLock) { + mCurrentUserId = userId; + mCurrentUser = curUser; + mCurrentParallelUsers = parallelUsers; + mCurrentParallelUserIds = parallelUserIds; + mCurrentProfileUsers = profileUsers; + mCurrentProfileUserIds = profileUserIds; + } + } + + private void broadcastChange() { + int userId; + synchronized (mLock) { + userId = mCurrentUserId; + } + + Intent intent = new Intent(Intent.ACTION_PARALLEL_SPACE_CHANGED); + intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); + getContext().sendBroadcastAsUser(intent, UserHandle.of(userId), + android.Manifest.permission.MANAGE_PARALLEL_SPACES); + } + + private Set getNonRequiredApps(int userId) { + Intent launcherIntent = new Intent(Intent.ACTION_MAIN); + launcherIntent.addCategory(Intent.CATEGORY_LAUNCHER); + List resolveInfos = mPackageManager.queryIntentActivitiesAsUser( + launcherIntent, + PackageManager.MATCH_SYSTEM_ONLY | PackageManager.MATCH_DISABLED_COMPONENTS, + userId); + Set apps = new ArraySet<>(); + + for (ResolveInfo resolveInfo : resolveInfos) { + apps.add(resolveInfo.activityInfo.packageName); + } + apps.removeAll(SPACE_WHITELIST_PACKAGES); + apps.removeAll(Arrays.asList(getContext().getResources().getStringArray( + com.android.internal.R.array.config_parallelSpaceWhitelist))); + // Those packages should be handled by GmsManagerService, always install them. + apps.removeAll(Arrays.asList(GmsManagerService.GMS_PACKAGES)); + apps.addAll(SPACE_BLOCKLIST_PACKAGES); + apps.addAll(Arrays.asList(getContext().getResources().getStringArray( + com.android.internal.R.array.config_parallelSpaceBlocklist))); + + Slog.i(TAG, "Package installation skipped: " + apps); + return apps; + } + + @Override + public void onUserStarting(TargetUser user) { + synchronized (mLock) { + // Not parallel user. + if (mCurrentParallelUserIds == null || + !mCurrentParallelUserIds.contains(user.getUserIdentifier())) + return; + } + mHandler.sendMessage(mHandler.obtainMessage(MSG_SETUP_SPACE, user.getUserIdentifier(), 0)); + } + + @Override + public void onUserSwitching(TargetUser from, TargetUser to) { + mHandler.sendMessage(mHandler.obtainMessage(MSG_UPDATE_PARALLEL_USER_LIST)); + mHandler.removeMessages(MSG_START_SPACES); + mHandler.sendMessage(mHandler.obtainMessage(MSG_START_SPACES)); + } + + @Override + public void onUserStopped(TargetUser user) { + // Wake up spaces if stopped by incidents e.g. stopUserOnSwitch. + // Delay it in case this is a stop before removing, so we need to wait for + // the user removed broadcast first otherwise it will crash the system. + mHandler.removeMessages(MSG_START_SPACES); + mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_START_SPACES), RESTART_USER_DELAY); + } + + @Override + public void onBootPhase(int phase) { + if (phase != PHASE_ACTIVITY_MANAGER_READY) + return; + mHandler.sendMessage(mHandler.obtainMessage(MSG_UPDATE_PARALLEL_USER_LIST)); + } + + @Override + public void onStart() { + mUserManager = IUserManager.Stub.asInterface( + ServiceManager.getService(Context.USER_SERVICE)); + mActivityManager = IActivityManager.Stub.asInterface( + ServiceManager.getService(Context.ACTIVITY_SERVICE)); + mPackageManager = getContext().getPackageManager(); + mPackageManagerService = IPackageManager.Stub.asInterface( + ServiceManager.getService("package")); + try { + mPackageInstaller = mPackageManagerService.getPackageInstaller(); + } catch (RemoteException e) { + throw new RuntimeException("Unable to get package installer"); + } + + ServiceThread st = new ServiceThread(TAG, THREAD_PRIORITY_DEFAULT, false); + st.start(); + mHandler = new Handler(st.getLooper()) { + @Override + public void handleMessage(Message msg) { + handleMessageInternal(msg); + } + }; + + IntentFilter unlockFilter = new IntentFilter(); + unlockFilter.addAction(Intent.ACTION_USER_UNLOCKED); + getContext().registerReceiverForAllUsers( + new FirstUnlockReceiver(), unlockFilter, null, mHandler); + + IntentFilter userFilter = new IntentFilter(); + userFilter.addAction(Intent.ACTION_USER_ADDED); + userFilter.addAction(Intent.ACTION_USER_REMOVED); + getContext().registerReceiverForAllUsers( + new UserReceiver(), userFilter, null, mHandler); + + mInterface = new Interface(); + publishBinderService(SERVICE_NAME, mInterface); + } + + public ParallelSpaceManagerService(Context context) { + super(context); + } + + private void ensureParallelUser(int userId) { + synchronized (mLock) { + if (mCurrentParallelUserIds == null || !mCurrentParallelUserIds.contains(userId)) + throw new IllegalArgumentException(userId + " is not a parallel space"); + } + } + + private boolean hasPermissionGranted(String permission, int uid) { + return ActivityManager.checkComponentPermission( + permission, uid, /* owningUid = */-1, /* exported = */ true) == + PackageManager.PERMISSION_GRANTED; + } + + private void enforceCallingPermission(int uid) { + if (uid == Process.SYSTEM_UID || uid == Process.SHELL_UID) + return; + if (!hasPermissionGranted(android.Manifest.permission.MANAGE_PARALLEL_SPACES, uid)) + throw new SecurityException("Caller does not have permission MANAGE_PARALLEL_SPACES"); + } + + private class Interface extends IParallelSpaceManager.Stub { + @Override + public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, + String[] args, ShellCallback callback, ResultReceiver resultReceiver) { + (new DebugShellCommand()).exec(this, in, out, err, args, callback, resultReceiver); + } + + @Override + protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) { + synchronized (mLock) { + fout.println("mCurrentUserId=" + mCurrentUserId); + fout.println("mCurrentUser=" + mCurrentUser); + fout.println("mCurrentParallelUsers=" + mCurrentParallelUsers); + fout.println("mCurrentParallelUserIds=" + mCurrentParallelUserIds); + fout.println("mCurrentProfileUsers=" + mCurrentProfileUsers); + fout.println("mCurrentProfileUserIds=" + mCurrentProfileUserIds); + } + } + + @Override + public int create(String name, boolean shareMedia) { + enforceCallingPermission(Binder.getCallingUid()); + + final long token = Binder.clearCallingIdentity(); + try { + return createSpaceInternal(name, shareMedia); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public int remove(int userId) { + enforceCallingPermission(Binder.getCallingUid()); + ensureParallelUser(userId); + + final long token = Binder.clearCallingIdentity(); + try { + return removeSpaceInternal(userId); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public UserInfo[] getUsers() { + enforceCallingPermission(Binder.getCallingUid()); + synchronized (mLock) { + if (mCurrentParallelUsers != null) + return mCurrentParallelUsers.toArray(new UserInfo[0]); + return new UserInfo[0]; + } + } + + @Override + public UserInfo getOwner() { + enforceCallingPermission(Binder.getCallingUid()); + synchronized (mLock) { + return mCurrentUser; + } + } + + @Override + public int duplicatePackage(String packageName, int userId) { + enforceCallingPermission(Binder.getCallingUid()); + ensureParallelUser(userId); + // Forward it to PM with full permission. + final long token = Binder.clearCallingIdentity(); + try { + int result = mPackageManager.installExistingPackageAsUser(packageName, userId); + return result == PackageManager.INSTALL_SUCCEEDED ? 0 : -1; + } catch (PackageManager.NameNotFoundException e) { + return -1; + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public int removePackage(String packageName, int userId) { + enforceCallingPermission(Binder.getCallingUid()); + ensureParallelUser(userId); + final long token = Binder.clearCallingIdentity(); + try { + // PM does not provide proper api, so use PMI instead. + PackageInstallerReceiver receiver = new PackageInstallerReceiver(); + mPackageInstaller.uninstallExistingPackage( + new VersionedPackage(packageName, PackageManager.VERSION_CODE_HIGHEST), + getContext().getPackageName(), receiver.getIntentSender(), userId); + // Can be a little bit tricky to get the result. + return receiver.waitForIntResult() == PackageInstaller.STATUS_SUCCESS ? 0 : -1; + } catch (RemoteException e) { + return -1; + } finally { + Binder.restoreCallingIdentity(token); + } + } + } + + private final class UserReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + // No need for broadcast because this is only for work profile. + updateParallelUserListInternalNoBroadcast(); + } + } + + private final class FirstUnlockReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + // Must wait until unlock otherwise it'll break storage session. + mHandler.sendMessage(mHandler.obtainMessage(MSG_START_SPACES)); + // This should only be run once. + getContext().unregisterReceiver(this); + } + } + + private class DebugShellCommand extends ShellCommand { + @Override + public int onCommand(String cmd) { + boolean handled = false; + int result = 0; + PrintWriter pw = getOutPrintWriter(); + + if ("create".equals(cmd)) { + result = mInterface.create(getNextArg(), "share".equals(getNextArg())); + handled = true; + } else if ("remove".equals(cmd)) { + result = mInterface.remove(Integer.parseInt(getNextArg())); + handled = true; + } else if ("list".equals(cmd)) { + for (UserInfo user : mInterface.getUsers()) { + pw.println(user.toFullString()); + } + handled = true; + } else if ("dup".equals(cmd)) { + result = mInterface.duplicatePackage(getNextArg(), Integer.parseInt(getNextArg())); + handled = true; + } else if ("rmdup".equals(cmd)) { + result = mInterface.removePackage(getNextArg(), Integer.parseInt(getNextArg())); + handled = true; + } + + if (handled) { + pw.println("Command done result = " + result); + return 0; + } + return handleDefaultCommands(cmd); + } + + @Override + public void onHelp() { + PrintWriter pw = getOutPrintWriter(); + } + } + + private static class PackageInstallerReceiver { + private Object mLock = new Object(); + @GuardedBy("mLock") + private Intent mResult; + + private IIntentSender.Stub mLocalSender = new IIntentSender.Stub() { + @Override + public void send(int code, Intent intent, String resolvedType, IBinder whitelistToken, + IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) { + synchronized (mLock) { + mResult = intent; + mLock.notifyAll(); + } + } + }; + + public IntentSender getIntentSender() { + return new IntentSender((IIntentSender) mLocalSender); + } + + private Intent waitForResult() { + synchronized (mLock) { + if (mResult != null) + return mResult; + try { + mLock.wait(); + return mResult; + } catch (InterruptedException e) { + return null; + } + } + } + + public int waitForIntResult() { + Intent result = waitForResult(); + if (result == null) + return PackageInstaller.STATUS_FAILURE; + return result.getIntExtra( + PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_FAILURE); + } + } +} diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 2ee4af56d320..a57d2f1b05c0 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -167,6 +167,8 @@ import com.android.server.pm.dex.OdsignStatsLogger; import com.android.server.pm.dex.SystemServerDexLoadReporter; import com.android.server.pm.verify.domain.DomainVerificationService; +import com.android.server.pocket.PocketService; +import com.android.server.pocket.PocketBridgeService; import com.android.server.policy.AppOpsPolicy; import com.android.server.policy.PermissionPolicyService; import com.android.server.policy.PhoneWindowManager; @@ -216,6 +218,9 @@ import dalvik.system.VMRuntime; +import ink.kaleidoscope.server.GmsManagerService; +import ink.kaleidoscope.server.ParallelSpaceManagerService; + import java.io.File; import java.io.FileDescriptor; import java.io.IOException; @@ -402,6 +407,10 @@ public final class SystemServer implements Dumpable { "com.android.server.media.MediaCommunicationService"; private static final String APP_COMPAT_OVERRIDES_SERVICE_CLASS = "com.android.server.compat.overrides.AppCompatOverridesService$Lifecycle"; + private static final String GMS_MANAGER_SERVICE_SERVICE_CLASS = + "ink.kaleidoscope.server.GmsManagerService"; + private static final String PARALLEL_SPACE_SERVICE_CLASS = + "ink.kaleidoscope.server.ParallelSpaceManagerService"; private static final String ROLE_SERVICE_CLASS = "com.android.role.RoleService"; private static final String GAME_MANAGER_SERVICE_CLASS = @@ -423,6 +432,9 @@ public final class SystemServer implements Dumpable { private static final String TETHERING_CONNECTOR_CLASS = "android.net.ITetheringConnector"; + private static final String APP_LOCK_SERVICE_CLASS = + "com.android.server.app.AppLockManagerService$Lifecycle"; + private static final String PERSISTENT_DATA_BLOCK_PROP = "ro.frp.pst"; private static final String UNCRYPT_PACKAGE_FILE = "/cache/recovery/uncrypt_file"; @@ -484,6 +496,8 @@ public final class SystemServer implements Dumpable { /** Start the IStats services. This is a blocking call and can take time. */ private static native void startIStatsService(); + public boolean safeMode = false; + /** * Start the memtrack proxy service. */ @@ -1006,7 +1020,7 @@ private void performPendingShutdown() { Slog.e(TAG, "Error reading uncrypt package file", e); } - if (filename != null && filename.startsWith("/data")) { + if (filename != null && filename.startsWith("/data") && SystemProperties.get("persist.sys.recovery_update", "").equals("true")) { if (!new File(BLOCK_MAP_FILE).exists()) { Slog.e(TAG, "Can't find block map file, uncrypt failed or " + "unexpected runtime restart?"); @@ -1673,7 +1687,10 @@ private void startOtherServices(@NonNull TimingsTraceAndSlog t) { // Before things start rolling, be sure we have decided whether // we are in safe mode. - final boolean safeMode = wm.detectSafeMode(); + + if(wm != null) { + safeMode = wm.detectSafeMode(); + } if (safeMode) { // If yes, immediately turn on the global setting for airplane mode. // Note that this does not send broadcasts at this stage because @@ -2505,6 +2522,10 @@ private void startOtherServices(@NonNull TimingsTraceAndSlog t) { mSystemServiceManager.startService(CrossProfileAppsService.class); t.traceEnd(); + t.traceBegin("StartPocketService"); + mSystemServiceManager.startService(PocketService.class); + t.traceEnd(); + t.traceBegin("StartPeopleService"); mSystemServiceManager.startService(PeopleService.class); t.traceEnd(); @@ -2512,6 +2533,14 @@ private void startOtherServices(@NonNull TimingsTraceAndSlog t) { t.traceBegin("StartMediaMetricsManager"); mSystemServiceManager.startService(MediaMetricsManagerService.class); t.traceEnd(); + + // PocketBridge + if (!context.getResources().getString( + com.android.internal.R.string.config_pocketBridgeSysfsInpocket).isEmpty()) { + t.traceBegin("StartPocketBridgeService"); + mSystemServiceManager.startService(PocketBridgeService.class); + t.traceEnd(); + } } t.traceBegin("StartMediaProjectionManager"); @@ -2744,6 +2773,10 @@ private void startOtherServices(@NonNull TimingsTraceAndSlog t) { t.traceEnd(); } + t.traceBegin("AppLockManagerService"); + mSystemServiceManager.startService(APP_LOCK_SERVICE_CLASS); + t.traceEnd(); + t.traceBegin("StartBootPhaseDeviceSpecificServicesReady"); mSystemServiceManager.startBootPhase(t, SystemService.PHASE_DEVICE_SPECIFIC_SERVICES_READY); t.traceEnd(); @@ -2770,6 +2803,14 @@ private void startOtherServices(@NonNull TimingsTraceAndSlog t) { mSystemServiceManager.startService(APP_COMPAT_OVERRIDES_SERVICE_CLASS); t.traceEnd(); + t.traceBegin("StartGmsManagerService"); + mSystemServiceManager.startService(GMS_MANAGER_SERVICE_SERVICE_CLASS); + t.traceEnd(); + + t.traceBegin("StartParallelSpaceManagerService"); + mSystemServiceManager.startService(PARALLEL_SPACE_SERVICE_CLASS); + t.traceEnd(); + // These are needed to propagate to the runnable below. final NetworkManagementService networkManagementF = networkManagement; final NetworkPolicyManagerService networkPolicyF = networkPolicy; diff --git a/services/people/java/com/android/server/people/PeopleService.java b/services/people/java/com/android/server/people/PeopleService.java index eab3b770a94a..292320e498a3 100644 --- a/services/people/java/com/android/server/people/PeopleService.java +++ b/services/people/java/com/android/server/people/PeopleService.java @@ -53,6 +53,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.function.Consumer; /** @@ -372,7 +373,8 @@ public String getShortcutId() { @Override public boolean equals(Object o) { ListenerKey key = (ListenerKey) o; - return key.getPackageName().equals(mPackageName) && key.getUserId() == mUserId + return key.getPackageName().equals(mPackageName) + && Objects.equals(key.getUserId(), mUserId) && key.getShortcutId().equals(mShortcutId); } diff --git a/services/tests/servicestests/src/com/android/server/camera/CameraServiceProxyTest.java b/services/tests/servicestests/src/com/android/server/camera/CameraServiceProxyTest.java index ea746d1f4fd3..faad961510d6 100644 --- a/services/tests/servicestests/src/com/android/server/camera/CameraServiceProxyTest.java +++ b/services/tests/servicestests/src/com/android/server/camera/CameraServiceProxyTest.java @@ -30,7 +30,7 @@ import android.view.Display; import android.view.Surface; -import java.util.HashMap; +import java.util.Map; @RunWith(JUnit4.class) public class CameraServiceProxyTest { @@ -75,24 +75,22 @@ public void testGetCropRotateScale() { /*ignoreResizableAndSdkCheck*/true)).isEqualTo( CameraMetadata.SCALER_ROTATE_AND_CROP_NONE); // Check rotation and lens facing combinations - HashMap backFacingMap = new HashMap() {{ - put(Surface.ROTATION_0, CameraMetadata.SCALER_ROTATE_AND_CROP_NONE); - put(Surface.ROTATION_90, CameraMetadata.SCALER_ROTATE_AND_CROP_90); - put(Surface.ROTATION_270, CameraMetadata.SCALER_ROTATE_AND_CROP_270); - put(Surface.ROTATION_180, CameraMetadata.SCALER_ROTATE_AND_CROP_180); - }}; + Map backFacingMap = Map.of( + Surface.ROTATION_0, CameraMetadata.SCALER_ROTATE_AND_CROP_NONE, + Surface.ROTATION_90, CameraMetadata.SCALER_ROTATE_AND_CROP_90, + Surface.ROTATION_270, CameraMetadata.SCALER_ROTATE_AND_CROP_270, + Surface.ROTATION_180, CameraMetadata.SCALER_ROTATE_AND_CROP_180); taskInfo.isFixedOrientationPortrait = true; backFacingMap.forEach((key, value) -> { assertThat(CameraServiceProxy.getCropRotateScale(ctx, ctx.getPackageName(), taskInfo, key, CameraCharacteristics.LENS_FACING_BACK, /*ignoreResizableAndSdkCheck*/true)).isEqualTo(value); }); - HashMap frontFacingMap = new HashMap() {{ - put(Surface.ROTATION_0, CameraMetadata.SCALER_ROTATE_AND_CROP_NONE); - put(Surface.ROTATION_90, CameraMetadata.SCALER_ROTATE_AND_CROP_270); - put(Surface.ROTATION_270, CameraMetadata.SCALER_ROTATE_AND_CROP_90); - put(Surface.ROTATION_180, CameraMetadata.SCALER_ROTATE_AND_CROP_180); - }}; + Map frontFacingMap = Map.of( + Surface.ROTATION_0, CameraMetadata.SCALER_ROTATE_AND_CROP_NONE, + Surface.ROTATION_90, CameraMetadata.SCALER_ROTATE_AND_CROP_270, + Surface.ROTATION_270, CameraMetadata.SCALER_ROTATE_AND_CROP_90, + Surface.ROTATION_180, CameraMetadata.SCALER_ROTATE_AND_CROP_180); frontFacingMap.forEach((key, value) -> { assertThat(CameraServiceProxy.getCropRotateScale(ctx, ctx.getPackageName(), taskInfo, key, CameraCharacteristics.LENS_FACING_FRONT, diff --git a/services/tests/servicestests/src/com/android/server/display/AmbientBrightnessStatsTrackerTest.java b/services/tests/servicestests/src/com/android/server/display/AmbientBrightnessStatsTrackerTest.java index df672c9f248d..2c4fe536b75c 100644 --- a/services/tests/servicestests/src/com/android/server/display/AmbientBrightnessStatsTrackerTest.java +++ b/services/tests/servicestests/src/com/android/server/display/AmbientBrightnessStatsTrackerTest.java @@ -424,7 +424,7 @@ public int getUserId(UserManager userManager, int userSerialNumber) { @Override public LocalDate getLocalDate() { - return LocalDate.from(mLocalDate); + return mLocalDate; } } diff --git a/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodSubtypeSwitchingControllerTest.java b/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodSubtypeSwitchingControllerTest.java index 9092ec325946..0884b784ac73 100644 --- a/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodSubtypeSwitchingControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodSubtypeSwitchingControllerTest.java @@ -367,6 +367,7 @@ public void testImeSubtypeListItem() throws Exception { assertFalse(item_en_us_allcaps.mIsSystemLocale); } + @SuppressWarnings("SelfComparison") @Test public void testImeSubtypeListComparator() throws Exception { final ComponentName imeX1 = new ComponentName("com.example.imeX", "Ime1"); diff --git a/services/tests/servicestests/src/com/android/server/locksettings/PasswordSlotManagerTestable.java b/services/tests/servicestests/src/com/android/server/locksettings/PasswordSlotManagerTestable.java index 1e855a9819ac..1eb4fa5439e6 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/PasswordSlotManagerTestable.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/PasswordSlotManagerTestable.java @@ -58,4 +58,4 @@ void cleanup() { } catch (Exception e) { } } -}; +} diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java index c5131c8f8c1d..57e403c21ac8 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java @@ -435,63 +435,112 @@ public void testImplicitGrant() { @Test public void testNotifyPostedLockedInLockdownMode() { - NotificationRecord r = mock(NotificationRecord.class); - NotificationRecord old = mock(NotificationRecord.class); - - // before the lockdown mode - when(mNm.isInLockDownMode()).thenReturn(false); - mListeners.notifyPostedLocked(r, old, true); - mListeners.notifyPostedLocked(r, old, false); - verify(r, atLeast(2)).getSbn(); - - // in the lockdown mode - reset(r); - reset(old); - when(mNm.isInLockDownMode()).thenReturn(true); - mListeners.notifyPostedLocked(r, old, true); - mListeners.notifyPostedLocked(r, old, false); - verify(r, never()).getSbn(); - } - - @Test - public void testnotifyRankingUpdateLockedInLockdownMode() { - List chn = mock(List.class); - - // before the lockdown mode - when(mNm.isInLockDownMode()).thenReturn(false); - mListeners.notifyRankingUpdateLocked(chn); - verify(chn, atLeast(1)).size(); - - // in the lockdown mode - reset(chn); - when(mNm.isInLockDownMode()).thenReturn(true); - mListeners.notifyRankingUpdateLocked(chn); - verify(chn, never()).size(); + NotificationRecord r0 = mock(NotificationRecord.class); + NotificationRecord old0 = mock(NotificationRecord.class); + UserHandle uh0 = mock(UserHandle.class); + + NotificationRecord r1 = mock(NotificationRecord.class); + NotificationRecord old1 = mock(NotificationRecord.class); + UserHandle uh1 = mock(UserHandle.class); + + // Neither user0 and user1 is in the lockdown mode + when(r0.getUser()).thenReturn(uh0); + when(uh0.getIdentifier()).thenReturn(0); + when(mNm.isInLockDownMode(0)).thenReturn(false); + + when(r1.getUser()).thenReturn(uh1); + when(uh1.getIdentifier()).thenReturn(1); + when(mNm.isInLockDownMode(1)).thenReturn(false); + + mListeners.notifyPostedLocked(r0, old0, true); + mListeners.notifyPostedLocked(r0, old0, false); + verify(r0, atLeast(2)).getSbn(); + + mListeners.notifyPostedLocked(r1, old1, true); + mListeners.notifyPostedLocked(r1, old1, false); + verify(r1, atLeast(2)).getSbn(); + + // Reset + reset(r0); + reset(old0); + reset(r1); + reset(old1); + + // Only user 0 is in the lockdown mode + when(r0.getUser()).thenReturn(uh0); + when(uh0.getIdentifier()).thenReturn(0); + when(mNm.isInLockDownMode(0)).thenReturn(true); + + when(r1.getUser()).thenReturn(uh1); + when(uh1.getIdentifier()).thenReturn(1); + when(mNm.isInLockDownMode(1)).thenReturn(false); + + mListeners.notifyPostedLocked(r0, old0, true); + mListeners.notifyPostedLocked(r0, old0, false); + verify(r0, never()).getSbn(); + + mListeners.notifyPostedLocked(r1, old1, true); + mListeners.notifyPostedLocked(r1, old1, false); + verify(r1, atLeast(2)).getSbn(); } @Test public void testNotifyRemovedLockedInLockdownMode() throws NoSuchFieldException { - NotificationRecord r = mock(NotificationRecord.class); - NotificationStats rs = mock(NotificationStats.class); + NotificationRecord r0 = mock(NotificationRecord.class); + NotificationStats rs0 = mock(NotificationStats.class); + UserHandle uh0 = mock(UserHandle.class); + + NotificationRecord r1 = mock(NotificationRecord.class); + NotificationStats rs1 = mock(NotificationStats.class); + UserHandle uh1 = mock(UserHandle.class); + StatusBarNotification sbn = mock(StatusBarNotification.class); FieldSetter.setField(mNm, NotificationManagerService.class.getDeclaredField("mHandler"), mock(NotificationManagerService.WorkerHandler.class)); - // before the lockdown mode - when(mNm.isInLockDownMode()).thenReturn(false); - when(r.getSbn()).thenReturn(sbn); - mListeners.notifyRemovedLocked(r, 0, rs); - mListeners.notifyRemovedLocked(r, 0, rs); - verify(r, atLeast(2)).getSbn(); - - // in the lockdown mode - reset(r); - reset(rs); - when(mNm.isInLockDownMode()).thenReturn(true); - when(r.getSbn()).thenReturn(sbn); - mListeners.notifyRemovedLocked(r, 0, rs); - mListeners.notifyRemovedLocked(r, 0, rs); - verify(r, never()).getSbn(); + // Neither user0 and user1 is in the lockdown mode + when(r0.getUser()).thenReturn(uh0); + when(uh0.getIdentifier()).thenReturn(0); + when(mNm.isInLockDownMode(0)).thenReturn(false); + when(r0.getSbn()).thenReturn(sbn); + + when(r1.getUser()).thenReturn(uh1); + when(uh1.getIdentifier()).thenReturn(1); + when(mNm.isInLockDownMode(1)).thenReturn(false); + when(r1.getSbn()).thenReturn(sbn); + + mListeners.notifyRemovedLocked(r0, 0, rs0); + mListeners.notifyRemovedLocked(r0, 0, rs0); + verify(r0, atLeast(2)).getSbn(); + + mListeners.notifyRemovedLocked(r1, 0, rs1); + mListeners.notifyRemovedLocked(r1, 0, rs1); + verify(r1, atLeast(2)).getSbn(); + + // Reset + reset(r0); + reset(rs0); + reset(r1); + reset(rs1); + + // Only user 0 is in the lockdown mode + when(r0.getUser()).thenReturn(uh0); + when(uh0.getIdentifier()).thenReturn(0); + when(mNm.isInLockDownMode(0)).thenReturn(true); + when(r0.getSbn()).thenReturn(sbn); + + when(r1.getUser()).thenReturn(uh1); + when(uh1.getIdentifier()).thenReturn(1); + when(mNm.isInLockDownMode(1)).thenReturn(false); + when(r1.getSbn()).thenReturn(sbn); + + mListeners.notifyRemovedLocked(r0, 0, rs0); + mListeners.notifyRemovedLocked(r0, 0, rs0); + verify(r0, never()).getSbn(); + + mListeners.notifyRemovedLocked(r1, 0, rs1); + mListeners.notifyRemovedLocked(r1, 0, rs1); + verify(r1, atLeast(2)).getSbn(); } } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index a545c8344c27..3f3b052931ab 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -174,6 +174,7 @@ import android.service.notification.ConversationChannelWrapper; import android.service.notification.NotificationListenerFilter; import android.service.notification.NotificationListenerService; +import android.service.notification.NotificationRankingUpdate; import android.service.notification.NotificationStats; import android.service.notification.StatusBarNotification; import android.service.notification.ZenPolicy; @@ -7565,9 +7566,31 @@ public void testAddAutomaticZenRule_systemCallTakesPackageFromOwner() throws Exc verify(mockZenModeHelper).addAutomaticZenRule(eq("android"), eq(rule), anyString()); } + @Test + public void testAddAutomaticZenRule_systemAppIdCallTakesPackageFromOwner() throws Exception { + // The multi-user case: where the calling uid doesn't match the system uid, but the calling + // *appid* is the system. + mService.isSystemUid = false; + mService.isSystemAppId = true; + ZenModeHelper mockZenModeHelper = mock(ZenModeHelper.class); + when(mConditionProviders.isPackageOrComponentAllowed(anyString(), anyInt())) + .thenReturn(true); + mService.setZenHelper(mockZenModeHelper); + ComponentName owner = new ComponentName("android", "ProviderName"); + ZenPolicy zenPolicy = new ZenPolicy.Builder().allowAlarms(true).build(); + boolean isEnabled = true; + AutomaticZenRule rule = new AutomaticZenRule("test", owner, owner, mock(Uri.class), + zenPolicy, NotificationManager.INTERRUPTION_FILTER_PRIORITY, isEnabled); + mBinderService.addAutomaticZenRule(rule, "com.android.settings"); + + // verify that zen mode helper gets passed in a package name of "android" + verify(mockZenModeHelper).addAutomaticZenRule(eq("android"), eq(rule), anyString()); + } + @Test public void testAddAutomaticZenRule_nonSystemCallTakesPackageFromArg() throws Exception { mService.isSystemUid = false; + mService.isSystemAppId = false; ZenModeHelper mockZenModeHelper = mock(ZenModeHelper.class); when(mConditionProviders.isPackageOrComponentAllowed(anyString(), anyInt())) .thenReturn(true); @@ -9781,10 +9804,10 @@ public void testStrongAuthTracker_isInLockDownMode() { mStrongAuthTracker.setGetStrongAuthForUserReturnValue( STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN); mStrongAuthTracker.onStrongAuthRequiredChanged(mContext.getUserId()); - assertTrue(mStrongAuthTracker.isInLockDownMode()); - mStrongAuthTracker.setGetStrongAuthForUserReturnValue(0); + assertTrue(mStrongAuthTracker.isInLockDownMode(mContext.getUserId())); + mStrongAuthTracker.setGetStrongAuthForUserReturnValue(mContext.getUserId()); mStrongAuthTracker.onStrongAuthRequiredChanged(mContext.getUserId()); - assertFalse(mStrongAuthTracker.isInLockDownMode()); + assertFalse(mStrongAuthTracker.isInLockDownMode(mContext.getUserId())); } @Test @@ -9800,8 +9823,8 @@ public void testCancelAndPostNotificationsWhenEnterAndExitLockDownMode() { // when entering the lockdown mode, cancel the 2 notifications. mStrongAuthTracker.setGetStrongAuthForUserReturnValue( STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN); - mStrongAuthTracker.onStrongAuthRequiredChanged(mContext.getUserId()); - assertTrue(mStrongAuthTracker.isInLockDownMode()); + mStrongAuthTracker.onStrongAuthRequiredChanged(0); + assertTrue(mStrongAuthTracker.isInLockDownMode(0)); // the notifyRemovedLocked function is called twice due to REASON_CANCEL_ALL. ArgumentCaptor captor = ArgumentCaptor.forClass(Integer.class); @@ -9810,10 +9833,46 @@ public void testCancelAndPostNotificationsWhenEnterAndExitLockDownMode() { // exit lockdown mode. mStrongAuthTracker.setGetStrongAuthForUserReturnValue(0); - mStrongAuthTracker.onStrongAuthRequiredChanged(mContext.getUserId()); + mStrongAuthTracker.onStrongAuthRequiredChanged(0); + assertFalse(mStrongAuthTracker.isInLockDownMode(0)); // the notifyPostedLocked function is called twice. - verify(mListeners, times(2)).notifyPostedLocked(any(), any()); + verify(mWorkerHandler, times(2)).postDelayed(any(Runnable.class), anyLong()); + //verify(mListeners, times(2)).notifyPostedLocked(any(), any()); + } + + @Test + public void testMakeRankingUpdateLockedInLockDownMode() { + // post 2 notifications from a same package + NotificationRecord pkgA = new NotificationRecord(mContext, + generateSbn("a", 1000, 9, 0), mTestNotificationChannel); + mService.addNotification(pkgA); + NotificationRecord pkgB = new NotificationRecord(mContext, + generateSbn("a", 1000, 9, 1), mTestNotificationChannel); + mService.addNotification(pkgB); + + mService.setIsVisibleToListenerReturnValue(true); + NotificationRankingUpdate nru = mService.makeRankingUpdateLocked(null); + assertEquals(2, nru.getRankingMap().getOrderedKeys().length); + + // when only user 0 entering the lockdown mode, its notification will be suppressed. + mStrongAuthTracker.setGetStrongAuthForUserReturnValue( + STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN); + mStrongAuthTracker.onStrongAuthRequiredChanged(0); + assertTrue(mStrongAuthTracker.isInLockDownMode(0)); + assertFalse(mStrongAuthTracker.isInLockDownMode(1)); + + nru = mService.makeRankingUpdateLocked(null); + assertEquals(1, nru.getRankingMap().getOrderedKeys().length); + + // User 0 exits lockdown mode. Its notification will be resumed. + mStrongAuthTracker.setGetStrongAuthForUserReturnValue(0); + mStrongAuthTracker.onStrongAuthRequiredChanged(0); + assertFalse(mStrongAuthTracker.isInLockDownMode(0)); + assertFalse(mStrongAuthTracker.isInLockDownMode(1)); + + nru = mService.makeRankingUpdateLocked(null); + assertEquals(2, nru.getRankingMap().getOrderedKeys().length); } @Test diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java index 598a22bbde39..d46530c27690 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java @@ -2727,7 +2727,7 @@ public void testCreateChannel_updateName() { @Test public void testCreateChannel_addToGroup() { - NotificationChannelGroup group = new NotificationChannelGroup("group", ""); + NotificationChannelGroup group = new NotificationChannelGroup("group", "group"); mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, group, true); NotificationChannel nc = new NotificationChannel("id", "hello", IMPORTANCE_DEFAULT); assertTrue(mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, nc, true, false)); @@ -3177,8 +3177,8 @@ public void testIsGroup_appCannotResetBlock() throws Exception { @Test public void testGetNotificationChannelGroupWithChannels() throws Exception { - NotificationChannelGroup group = new NotificationChannelGroup("group", ""); - NotificationChannelGroup other = new NotificationChannelGroup("something else", ""); + NotificationChannelGroup group = new NotificationChannelGroup("group", "group"); + NotificationChannelGroup other = new NotificationChannelGroup("something else", "name"); mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, group, true); mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, other, true); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java b/services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java index b49e5cbfa9dc..61a6985d473e 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java @@ -19,10 +19,12 @@ import android.companion.ICompanionDeviceManager; import android.content.ComponentName; import android.content.Context; +import android.service.notification.StatusBarNotification; import androidx.annotation.Nullable; import com.android.internal.logging.InstanceIdSequence; +import com.android.server.notification.ManagedServices.ManagedServiceInfo; import java.util.HashSet; import java.util.Set; @@ -30,6 +32,7 @@ public class TestableNotificationManagerService extends NotificationManagerService { int countSystemChecks = 0; boolean isSystemUid = true; + boolean isSystemAppId = true; int countLogSmartSuggestionsVisible = 0; Set mChannelToastsSent = new HashSet<>(); @@ -37,6 +40,9 @@ public class TestableNotificationManagerService extends NotificationManagerServi @Nullable NotificationAssistantAccessGrantedCallback mNotificationAssistantAccessGrantedCallback; + @Nullable + Boolean mIsVisibleToListenerReturnValue = null; + TestableNotificationManagerService(Context context, NotificationRecordLogger logger, InstanceIdSequence notificationInstanceIdSequence) { super(context, logger, notificationInstanceIdSequence); @@ -52,6 +58,12 @@ protected boolean isCallingUidSystem() { return isSystemUid; } + @Override + protected boolean isCallingAppIdSystem() { + countSystemChecks++; + return isSystemUid || isSystemAppId; + } + @Override protected boolean isCallerSystemOrPhone() { countSystemChecks++; @@ -119,6 +131,19 @@ protected void setShowReviewPermissionsNotification(boolean setting) { mShowReviewPermissionsNotification = setting; } + protected void setIsVisibleToListenerReturnValue(boolean value) { + mIsVisibleToListenerReturnValue = value; + } + + @Override + boolean isVisibleToListener(StatusBarNotification sbn, int notificationType, + ManagedServiceInfo listener) { + if (mIsVisibleToListenerReturnValue != null) { + return mIsVisibleToListenerReturnValue; + } + return super.isVisibleToListener(sbn, notificationType, listener); + } + public class StrongAuthTrackerFake extends NotificationManagerService.StrongAuthTracker { private int mGetStrongAuthForUserReturnValue = 0; StrongAuthTrackerFake(Context context) { diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java index adf694c2a88d..0462e1be7a5f 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java @@ -30,6 +30,7 @@ import static android.content.pm.ActivityInfo.LAUNCH_MULTIPLE; import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE; import static android.content.res.Configuration.ORIENTATION_PORTRAIT; +import static android.os.Process.NOBODY_UID; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; @@ -1220,20 +1221,34 @@ public void testLastSnapshotData_notSet() { @Test public void testCreateRecentTaskInfo_detachedTask() { - final Task task = createTaskBuilder(".Task").setCreateActivity(true).build(); + final Task task = createTaskBuilder(".Task").build(); + new ActivityBuilder(mSupervisor.mService) + .setTask(task) + .setUid(NOBODY_UID) + .setComponent(getUniqueComponentName()) + .build(); final TaskDisplayArea tda = task.getDisplayArea(); assertTrue(task.isAttached()); assertTrue(task.supportsMultiWindow()); - RecentTaskInfo info = mRecentTasks.createRecentTaskInfo(task, true); + RecentTaskInfo info = mRecentTasks.createRecentTaskInfo(task, true /* stripExtras */, + true /* getTasksAllowed */); assertTrue(info.supportsMultiWindow); + info = mRecentTasks.createRecentTaskInfo(task, true /* stripExtras */, + false /* getTasksAllowed */); + + assertTrue(info.topActivity == null); + assertTrue(info.topActivityInfo == null); + assertTrue(info.baseActivity == null); + // The task can be put in split screen even if it is not attached now. task.removeImmediately(); - info = mRecentTasks.createRecentTaskInfo(task, true); + info = mRecentTasks.createRecentTaskInfo(task, true /* stripExtras */, + true /* getTasksAllowed */); assertTrue(info.supportsMultiWindow); @@ -1242,7 +1257,8 @@ public void testCreateRecentTaskInfo_detachedTask() { doReturn(false).when(tda).supportsNonResizableMultiWindow(); doReturn(false).when(task).isResizeable(); - info = mRecentTasks.createRecentTaskInfo(task, true); + info = mRecentTasks.createRecentTaskInfo(task, true /* stripExtras */, + true /* getTasksAllowed */); assertFalse(info.supportsMultiWindow); @@ -1250,7 +1266,8 @@ public void testCreateRecentTaskInfo_detachedTask() { // the device supports it. doReturn(true).when(tda).supportsNonResizableMultiWindow(); - info = mRecentTasks.createRecentTaskInfo(task, true); + info = mRecentTasks.createRecentTaskInfo(task, true /* stripExtras */, + true /* getTasksAllowed */); assertTrue(info.supportsMultiWindow); } diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java index 6ea416b54811..4edb615d769a 100644 --- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java +++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java @@ -61,6 +61,7 @@ import android.hidl.manager.V1_0.IServiceManager; import android.hidl.manager.V1_0.IServiceNotification; import android.os.BatteryManager; +import android.os.Build; import android.os.Environment; import android.os.FileUtils; import android.os.Handler; @@ -1302,7 +1303,7 @@ protected void updateUsbNotification(boolean force) { } Notification.Builder builder = new Notification.Builder(mContext, channel) - .setSmallIcon(com.android.internal.R.drawable.stat_sys_adb) + .setSmallIcon(com.android.internal.R.drawable.stat_sys_data_usb) .setWhen(0) .setOngoing(true) .setTicker(title) @@ -1312,6 +1313,7 @@ protected void updateUsbNotification(boolean force) { .system_notification_accent_color)) .setContentTitle(title) .setContentText(message) + .setSubText(Build.ID) .setContentIntent(pi) .setVisibility(Notification.VISIBILITY_PUBLIC); diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbVCInputTerminal.java b/services/usb/java/com/android/server/usb/descriptors/UsbVCInputTerminal.java index df637950899b..7a41b50e26bd 100644 --- a/services/usb/java/com/android/server/usb/descriptors/UsbVCInputTerminal.java +++ b/services/usb/java/com/android/server/usb/descriptors/UsbVCInputTerminal.java @@ -46,4 +46,4 @@ public void report(ReportCanvas canvas) { // TODO Add reporting specific to this descriptor super.report(canvas); } -}; +} diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbVCOutputTerminal.java b/services/usb/java/com/android/server/usb/descriptors/UsbVCOutputTerminal.java index 4aa8ca22cc4e..32275a60644c 100644 --- a/services/usb/java/com/android/server/usb/descriptors/UsbVCOutputTerminal.java +++ b/services/usb/java/com/android/server/usb/descriptors/UsbVCOutputTerminal.java @@ -46,4 +46,4 @@ public void report(ReportCanvas canvas) { super.report(canvas); // TODO Add reporting specific to this descriptor } -}; +} diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbVCProcessingUnit.java b/services/usb/java/com/android/server/usb/descriptors/UsbVCProcessingUnit.java index 5ce842e82598..0692066e1dee 100644 --- a/services/usb/java/com/android/server/usb/descriptors/UsbVCProcessingUnit.java +++ b/services/usb/java/com/android/server/usb/descriptors/UsbVCProcessingUnit.java @@ -47,4 +47,4 @@ public void report(ReportCanvas canvas) { super.report(canvas); // TODO Add reporting specific to this descriptor } -}; +} diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbVCSelectorUnit.java b/services/usb/java/com/android/server/usb/descriptors/UsbVCSelectorUnit.java index 8e9b0d886389..604dd66905da 100644 --- a/services/usb/java/com/android/server/usb/descriptors/UsbVCSelectorUnit.java +++ b/services/usb/java/com/android/server/usb/descriptors/UsbVCSelectorUnit.java @@ -47,4 +47,4 @@ public void report(ReportCanvas canvas) { super.report(canvas); // TODO Add reporting specific to this descriptor } -}; +} diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java index a6e1a3256cb6..5f5c5722de6b 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java @@ -1283,4 +1283,4 @@ private static void enforcePermissionForDataDelivery(@NonNull Context context, private static final String OP_MESSAGE = "Providing hotword detection result to VoiceInteractionService"; -}; +} diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java index a061618b1ca7..e02f2f00afa0 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java @@ -810,4 +810,4 @@ public void run() { } } }; -}; +} diff --git a/telephony/common/com/google/android/mms/pdu/EncodedStringValue.java b/telephony/common/com/google/android/mms/pdu/EncodedStringValue.java index 8b01cb3c4405..2787d83a99a3 100644 --- a/telephony/common/com/google/android/mms/pdu/EncodedStringValue.java +++ b/telephony/common/com/google/android/mms/pdu/EncodedStringValue.java @@ -199,7 +199,6 @@ public void appendTextString(byte[] textString) { */ @Override public Object clone() throws CloneNotSupportedException { - super.clone(); int len = mData.length; byte[] dstBytes = new byte[len]; System.arraycopy(mData, 0, dstBytes, 0, len); diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index cf6d681d596a..2c879d0bc098 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -8983,7 +8983,7 @@ private static PersistableBundle getDefaults() { sDefaults.putBoolean(KEY_SHOW_4G_FOR_3G_DATA_ICON_BOOL, false); sDefaults.putString(KEY_OPERATOR_NAME_FILTER_PATTERN_STRING, ""); sDefaults.putString(KEY_SHOW_CARRIER_DATA_ICON_PATTERN_STRING, ""); - sDefaults.putBoolean(KEY_HIDE_LTE_PLUS_DATA_ICON_BOOL, true); + sDefaults.putBoolean(KEY_HIDE_LTE_PLUS_DATA_ICON_BOOL, false); sDefaults.putInt(KEY_LTE_PLUS_THRESHOLD_BANDWIDTH_KHZ_INT, 20000); sDefaults.putInt(KEY_NR_ADVANCED_THRESHOLD_BANDWIDTH_KHZ_INT, 0); sDefaults.putIntArray(KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY, diff --git a/telephony/java/android/telephony/DataFailCause.java b/telephony/java/android/telephony/DataFailCause.java index 418f5185f84f..376b9ae9b3bf 100644 --- a/telephony/java/android/telephony/DataFailCause.java +++ b/telephony/java/android/telephony/DataFailCause.java @@ -1622,29 +1622,26 @@ public static boolean isPermanentFailure(@NonNull Context context, // If we are not able to find the configuration from carrier config, use the default // ones. if (permanentFailureSet == null) { - permanentFailureSet = new HashSet() { - { - add(OPERATOR_BARRED); - add(MISSING_UNKNOWN_APN); - add(UNKNOWN_PDP_ADDRESS_TYPE); - add(USER_AUTHENTICATION); - add(ACTIVATION_REJECT_GGSN); - add(SERVICE_OPTION_NOT_SUPPORTED); - add(SERVICE_OPTION_NOT_SUBSCRIBED); - add(NSAPI_IN_USE); - add(ONLY_IPV4_ALLOWED); - add(ONLY_IPV6_ALLOWED); - add(PROTOCOL_ERRORS); - add(RADIO_POWER_OFF); - add(TETHERED_CALL_ACTIVE); - add(RADIO_NOT_AVAILABLE); - add(UNACCEPTABLE_NETWORK_PARAMETER); - add(SIGNAL_LOST); - add(DUPLICATE_CID); - add(MATCH_ALL_RULE_NOT_ALLOWED); - add(ALL_MATCHING_RULES_FAILED); - } - }; + permanentFailureSet = new HashSet(); + permanentFailureSet.add(OPERATOR_BARRED); + permanentFailureSet.add(MISSING_UNKNOWN_APN); + permanentFailureSet.add(UNKNOWN_PDP_ADDRESS_TYPE); + permanentFailureSet.add(USER_AUTHENTICATION); + permanentFailureSet.add(ACTIVATION_REJECT_GGSN); + permanentFailureSet.add(SERVICE_OPTION_NOT_SUPPORTED); + permanentFailureSet.add(SERVICE_OPTION_NOT_SUBSCRIBED); + permanentFailureSet.add(NSAPI_IN_USE); + permanentFailureSet.add(ONLY_IPV4_ALLOWED); + permanentFailureSet.add(ONLY_IPV6_ALLOWED); + permanentFailureSet.add(PROTOCOL_ERRORS); + permanentFailureSet.add(RADIO_POWER_OFF); + permanentFailureSet.add(TETHERED_CALL_ACTIVE); + permanentFailureSet.add(RADIO_NOT_AVAILABLE); + permanentFailureSet.add(UNACCEPTABLE_NETWORK_PARAMETER); + permanentFailureSet.add(SIGNAL_LOST); + permanentFailureSet.add(DUPLICATE_CID); + permanentFailureSet.add(MATCH_ALL_RULE_NOT_ALLOWED); + permanentFailureSet.add(ALL_MATCHING_RULES_FAILED); } permanentFailureSet.add(NO_RETRY_FAILURE); diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index b6f86527b747..36066e957336 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -13971,7 +13971,6 @@ public boolean isEmergencyAssistanceEnabled() { @NonNull @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING) public Map> getEmergencyNumberList() { - Map> emergencyNumberList = new HashMap<>(); try { ITelephony telephony = getITelephony(); if (telephony != null) { @@ -13984,7 +13983,7 @@ public Map> getEmergencyNumberList() { Log.e(TAG, "getEmergencyNumberList RemoteException", ex); ex.rethrowAsRuntimeException(); } - return emergencyNumberList; + return Collections.emptyMap(); } /** diff --git a/telephony/java/android/telephony/ims/ImsService.java b/telephony/java/android/telephony/ims/ImsService.java index be233b82c426..977307b8c551 100644 --- a/telephony/java/android/telephony/ims/ImsService.java +++ b/telephony/java/android/telephony/ims/ImsService.java @@ -50,7 +50,6 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import java.util.HashMap; import java.util.Map; import java.util.concurrent.CancellationException; import java.util.concurrent.CompletableFuture; @@ -164,10 +163,9 @@ public class ImsService extends Service { * Used for logging purposes, see {@link #getCapabilitiesString(long)} * @hide */ - private static final Map CAPABILITIES_LOG_MAP = new HashMap() {{ - put(CAPABILITY_EMERGENCY_OVER_MMTEL, "EMERGENCY_OVER_MMTEL"); - put(CAPABILITY_SIP_DELEGATE_CREATION, "SIP_DELEGATE_CREATION"); - }}; + private static final Map CAPABILITIES_LOG_MAP = Map.of( + CAPABILITY_EMERGENCY_OVER_MMTEL, "EMERGENCY_OVER_MMTEL", + CAPABILITY_SIP_DELEGATE_CREATION, "SIP_DELEGATE_CREATION"); /** * The intent that must be defined as an intent-filter in the AndroidManifest of the ImsService. diff --git a/telephony/java/android/telephony/ims/RegistrationManager.java b/telephony/java/android/telephony/ims/RegistrationManager.java index 090d4136872e..9996b868afc7 100644 --- a/telephony/java/android/telephony/ims/RegistrationManager.java +++ b/telephony/java/android/telephony/ims/RegistrationManager.java @@ -78,24 +78,22 @@ public interface RegistrationManager { /**@hide*/ // Translate ImsRegistrationImplBase API to new AccessNetworkConstant because WLAN // and WWAN are more accurate constants. - Map IMS_REG_TO_ACCESS_TYPE_MAP = - new HashMap() {{ - // Map NONE to -1 to make sure that we handle the REGISTRATION_TECH_NONE - // case, since it is defined. - put(ImsRegistrationImplBase.REGISTRATION_TECH_NONE, - AccessNetworkConstants.TRANSPORT_TYPE_INVALID); - put(ImsRegistrationImplBase.REGISTRATION_TECH_LTE, - AccessNetworkConstants.TRANSPORT_TYPE_WWAN); - put(ImsRegistrationImplBase.REGISTRATION_TECH_NR, - AccessNetworkConstants.TRANSPORT_TYPE_WWAN); - put(ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN, - AccessNetworkConstants.TRANSPORT_TYPE_WLAN); - /* As the cross sim will be using ePDG tunnel over internet, it behaves - like IWLAN in most cases. Hence setting the access type as IWLAN - */ - put(ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM, - AccessNetworkConstants.TRANSPORT_TYPE_WLAN); - }}; + Map IMS_REG_TO_ACCESS_TYPE_MAP = Map.of( + // Map NONE to -1 to make sure that we handle the REGISTRATION_TECH_NONE + // case, since it is defined. + ImsRegistrationImplBase.REGISTRATION_TECH_NONE, + AccessNetworkConstants.TRANSPORT_TYPE_INVALID, + ImsRegistrationImplBase.REGISTRATION_TECH_LTE, + AccessNetworkConstants.TRANSPORT_TYPE_WWAN, + ImsRegistrationImplBase.REGISTRATION_TECH_NR, + AccessNetworkConstants.TRANSPORT_TYPE_WWAN, + ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN, + AccessNetworkConstants.TRANSPORT_TYPE_WLAN, + /* As the cross sim will be using ePDG tunnel over internet, it behaves + like IWLAN in most cases. Hence setting the access type as IWLAN + */ + ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM, + AccessNetworkConstants.TRANSPORT_TYPE_WLAN); /** @hide */ @NonNull diff --git a/telephony/java/android/telephony/ims/feature/ImsFeature.java b/telephony/java/android/telephony/ims/feature/ImsFeature.java index f5b158fedd37..6f23e6ebd733 100644 --- a/telephony/java/android/telephony/ims/feature/ImsFeature.java +++ b/telephony/java/android/telephony/ims/feature/ImsFeature.java @@ -34,7 +34,6 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import java.util.HashMap; import java.util.Map; /** @@ -85,11 +84,10 @@ public abstract class ImsFeature { * Used for logging purposes. * @hide */ - public static final Map FEATURE_LOG_MAP = new HashMap() {{ - put(FEATURE_EMERGENCY_MMTEL, "EMERGENCY_MMTEL"); - put(FEATURE_MMTEL, "MMTEL"); - put(FEATURE_RCS, "RCS"); - }}; + public static final Map FEATURE_LOG_MAP = Map.of( + FEATURE_EMERGENCY_MMTEL, "EMERGENCY_MMTEL", + FEATURE_MMTEL, "MMTEL", + FEATURE_RCS, "RCS"); /** * Integer values defining IMS features that are supported in ImsFeature. @@ -145,11 +143,10 @@ public abstract class ImsFeature { * Used for logging purposes. * @hide */ - public static final Map STATE_LOG_MAP = new HashMap() {{ - put(STATE_UNAVAILABLE, "UNAVAILABLE"); - put(STATE_INITIALIZING, "INITIALIZING"); - put(STATE_READY, "READY"); - }}; + public static final Map STATE_LOG_MAP = Map.of( + STATE_UNAVAILABLE, "UNAVAILABLE", + STATE_INITIALIZING, "INITIALIZING", + STATE_READY, "READY"); /** * Integer values defining the result codes that should be returned from diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java index b51e8d3d3c5d..dadcdb69c7dc 100644 --- a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java +++ b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java @@ -140,6 +140,17 @@ public boolean isTypeZero() { return (mProtocolIdentifier == 0x40); } + public static SmsMessage newFromCDS(byte[] pdu) { + try { + SmsMessage msg = new SmsMessage(); + msg.parsePdu(pdu); + return msg; + } catch (RuntimeException ex) { + Rlog.e(LOG_TAG, "CDS SMS PDU parsing failed: ", ex); + return null; + } + } + /** * Creates an SmsMessage from an SMS EF record. * diff --git a/tests/CanvasCompare/src/com/android/test/hwuicompare/DisplayModifier.java b/tests/CanvasCompare/src/com/android/test/hwuicompare/DisplayModifier.java index 0f4e122d147a..4bcf5a4e30d5 100644 --- a/tests/CanvasCompare/src/com/android/test/hwuicompare/DisplayModifier.java +++ b/tests/CanvasCompare/src/com/android/test/hwuicompare/DisplayModifier.java @@ -16,14 +16,16 @@ package com.android.test.hwuicompare; -import java.util.LinkedHashMap; -import java.util.Map.Entry; +import static java.util.Map.entry; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.RectF; import android.util.Log; +import java.util.Map; +import java.util.Map.Entry; + public abstract class DisplayModifier { // automated tests ignore any combination of operations that don't together return TOTAL_MASK @@ -76,41 +78,36 @@ public abstract class DisplayModifier { }; @SuppressWarnings("serial") - private static final LinkedHashMap> gMaps = new LinkedHashMap>() { - { - put("aa", new LinkedHashMap() { - { - put("true", new DisplayModifier() { + private static final Map> gMaps = Map.of( + "aa", Map.of( + "true", new DisplayModifier() { @Override public void modifyDrawing(Paint paint, Canvas canvas) { paint.setAntiAlias(true); } - }); - put("false", new DisplayModifier() { + }, + "false", new DisplayModifier() { @Override public void modifyDrawing(Paint paint, Canvas canvas) { paint.setAntiAlias(false); } - }); - } - }); - put("style", new LinkedHashMap() { - { - put("fill", new DisplayModifier() { + }), + "style", Map.of( + "fill", new DisplayModifier() { @Override public void modifyDrawing(Paint paint, Canvas canvas) { paint.setStyle(Paint.Style.FILL); } - }); - put("stroke", new DisplayModifier() { + }, + "stroke", new DisplayModifier() { @Override public void modifyDrawing(Paint paint, Canvas canvas) { paint.setStyle(Paint.Style.STROKE); } @Override protected int mask() { return SWEEP_STROKE_WIDTH_BIT; } - }); - put("fillAndStroke", new DisplayModifier() { + }, + "fillAndStroke", new DisplayModifier() { @Override public void modifyDrawing(Paint paint, Canvas canvas) { paint.setStyle(Paint.Style.FILL_AND_STROKE); @@ -118,131 +115,118 @@ public void modifyDrawing(Paint paint, Canvas canvas) { @Override protected int mask() { return SWEEP_STROKE_WIDTH_BIT; } - }); - } - }); - put("strokeWidth", new LinkedHashMap() { - { - put("hair", new DisplayModifier() { + }), + "strokeWidth", Map.of( + "hair", new DisplayModifier() { @Override public void modifyDrawing(Paint paint, Canvas canvas) { paint.setStrokeWidth(0); } @Override protected int mask() { return SWEEP_STROKE_WIDTH_BIT; } - }); - put("0.3", new DisplayModifier() { + }, + "0.3", new DisplayModifier() { @Override public void modifyDrawing(Paint paint, Canvas canvas) { paint.setStrokeWidth(0.3f); } - }); - put("1", new DisplayModifier() { + }, + "1", new DisplayModifier() { @Override public void modifyDrawing(Paint paint, Canvas canvas) { paint.setStrokeWidth(1); } - }); - put("5", new DisplayModifier() { + }, + "5", new DisplayModifier() { @Override public void modifyDrawing(Paint paint, Canvas canvas) { paint.setStrokeWidth(5); } - }); - put("30", new DisplayModifier() { + }, + "30", new DisplayModifier() { @Override public void modifyDrawing(Paint paint, Canvas canvas) { paint.setStrokeWidth(30); } - }); - } - }); - put("strokeCap", new LinkedHashMap() { - { - put("butt", new DisplayModifier() { + }), + "strokeCap", Map.of( + "butt", new DisplayModifier() { @Override public void modifyDrawing(Paint paint, Canvas canvas) { paint.setStrokeCap(Paint.Cap.BUTT); } @Override protected int mask() { return SWEEP_STROKE_CAP_BIT; } - }); - put("round", new DisplayModifier() { + }, + "round", new DisplayModifier() { @Override public void modifyDrawing(Paint paint, Canvas canvas) { paint.setStrokeCap(Paint.Cap.ROUND); } - }); - put("square", new DisplayModifier() { + }, + "square", new DisplayModifier() { @Override public void modifyDrawing(Paint paint, Canvas canvas) { paint.setStrokeCap(Paint.Cap.SQUARE); } - }); - } - }); - put("strokeJoin", new LinkedHashMap() { - { - put("bevel", new DisplayModifier() { + }), + "strokeJoin", Map.of( + "bevel", new DisplayModifier() { @Override public void modifyDrawing(Paint paint, Canvas canvas) { paint.setStrokeJoin(Paint.Join.BEVEL); } @Override protected int mask() { return SWEEP_STROKE_JOIN_BIT; } - }); - put("round", new DisplayModifier() { + }, + "round", new DisplayModifier() { @Override public void modifyDrawing(Paint paint, Canvas canvas) { paint.setStrokeJoin(Paint.Join.ROUND); } - }); - put("miter", new DisplayModifier() { + }, + "miter", new DisplayModifier() { @Override public void modifyDrawing(Paint paint, Canvas canvas) { paint.setStrokeJoin(Paint.Join.MITER); } - }); + }), // TODO: add miter0, miter1 etc to test miter distances - } - }); - - put("transform", new LinkedHashMap() { - { - put("noTransform", new DisplayModifier() { + "transform", Map.of( + "noTransform", new DisplayModifier() { @Override public void modifyDrawing(Paint paint, Canvas canvas) {} @Override protected int mask() { return SWEEP_TRANSFORM_BIT; }; - }); - put("rotate5", new DisplayModifier() { + }, + "rotate5", new DisplayModifier() { @Override public void modifyDrawing(Paint paint, Canvas canvas) { canvas.rotate(5); } - }); - put("rotate45", new DisplayModifier() { + }, + "rotate45", new DisplayModifier() { @Override public void modifyDrawing(Paint paint, Canvas canvas) { canvas.rotate(45); } - }); - put("rotate90", new DisplayModifier() { + }, + "rotate90", new DisplayModifier() { @Override public void modifyDrawing(Paint paint, Canvas canvas) { canvas.rotate(90); canvas.translate(0, -200); } - }); - put("scale2x2", new DisplayModifier() { + }, + "scale2x2", new DisplayModifier() { @Override public void modifyDrawing(Paint paint, Canvas canvas) { canvas.scale(2, 2); } @Override protected int mask() { return SWEEP_TRANSFORM_BIT; }; - }); - put("rot20scl1x4", new DisplayModifier() { + }, + "rot20scl1x4", new DisplayModifier() { @Override public void modifyDrawing(Paint paint, Canvas canvas) { canvas.rotate(20); @@ -250,180 +234,167 @@ public void modifyDrawing(Paint paint, Canvas canvas) { } @Override protected int mask() { return SWEEP_TRANSFORM_BIT; }; - }); - } - }); - - put("shader", new LinkedHashMap() { - { - put("noShader", new DisplayModifier() { + }), + "shader", Map.ofEntries( + entry("noShader", new DisplayModifier() { @Override public void modifyDrawing(Paint paint, Canvas canvas) {} @Override protected int mask() { return SWEEP_SHADER_BIT; }; - }); - put("repeatShader", new DisplayModifier() { + }), + entry("repeatShader", new DisplayModifier() { @Override public void modifyDrawing(Paint paint, Canvas canvas) { paint.setShader(ResourceModifiers.instance().mRepeatShader); } @Override protected int mask() { return SWEEP_SHADER_BIT; }; - }); - put("translatedShader", new DisplayModifier() { + }), + entry("translatedShader", new DisplayModifier() { @Override public void modifyDrawing(Paint paint, Canvas canvas) { paint.setShader(ResourceModifiers.instance().mTranslatedShader); } - }); - put("scaledShader", new DisplayModifier() { + }), + entry("scaledShader", new DisplayModifier() { @Override public void modifyDrawing(Paint paint, Canvas canvas) { paint.setShader(ResourceModifiers.instance().mScaledShader); } - }); - put("horGradient", new DisplayModifier() { + }), + entry("horGradient", new DisplayModifier() { @Override public void modifyDrawing(Paint paint, Canvas canvas) { paint.setShader(ResourceModifiers.instance().mHorGradient); } - }); - put("diagGradient", new DisplayModifier() { + }), + entry("diagGradient", new DisplayModifier() { @Override public void modifyDrawing(Paint paint, Canvas canvas) { paint.setShader(ResourceModifiers.instance().mDiagGradient); } @Override protected int mask() { return SWEEP_SHADER_BIT; }; - }); - put("vertGradient", new DisplayModifier() { + }), + entry("vertGradient", new DisplayModifier() { @Override public void modifyDrawing(Paint paint, Canvas canvas) { paint.setShader(ResourceModifiers.instance().mVertGradient); } - }); - put("radGradient", new DisplayModifier() { + }), + entry("radGradient", new DisplayModifier() { @Override public void modifyDrawing(Paint paint, Canvas canvas) { paint.setShader(ResourceModifiers.instance().mRadGradient); } - }); - put("sweepGradient", new DisplayModifier() { + }), + entry("sweepGradient", new DisplayModifier() { @Override public void modifyDrawing(Paint paint, Canvas canvas) { paint.setShader(ResourceModifiers.instance().mSweepGradient); } - }); - put("composeShader", new DisplayModifier() { + }), + entry("composeShader", new DisplayModifier() { @Override public void modifyDrawing(Paint paint, Canvas canvas) { paint.setShader(ResourceModifiers.instance().mComposeShader); } - }); - put("bad composeShader", new DisplayModifier() { + }), + entry("bad composeShader", new DisplayModifier() { @Override public void modifyDrawing(Paint paint, Canvas canvas) { paint.setShader(ResourceModifiers.instance().mBadComposeShader); } - }); - put("bad composeShader 2", new DisplayModifier() { + }), + entry("bad composeShader 2", new DisplayModifier() { @Override public void modifyDrawing(Paint paint, Canvas canvas) { paint.setShader(ResourceModifiers.instance().mAnotherBadComposeShader); } - }); - } - }); - - // FINAL MAP: DOES ACTUAL DRAWING - put("drawing", new LinkedHashMap() { - { - put("roundRect", new DisplayModifier() { + })), + "drawing", Map.ofEntries( + entry("roundRect", new DisplayModifier() { @Override public void modifyDrawing(Paint paint, Canvas canvas) { canvas.drawRoundRect(gRect, 20, 20, paint); } - }); - put("rect", new DisplayModifier() { + }), + entry("rect", new DisplayModifier() { @Override public void modifyDrawing(Paint paint, Canvas canvas) { canvas.drawRect(gRect, paint); } @Override protected int mask() { return SWEEP_SHADER_BIT | SWEEP_STROKE_CAP_BIT; }; - }); - put("circle", new DisplayModifier() { + }), + entry("circle", new DisplayModifier() { @Override public void modifyDrawing(Paint paint, Canvas canvas) { canvas.drawCircle(100, 100, 75, paint); } - }); - put("oval", new DisplayModifier() { + }), + entry("oval", new DisplayModifier() { @Override public void modifyDrawing(Paint paint, Canvas canvas) { canvas.drawOval(gRect, paint); } - }); - put("lines", new DisplayModifier() { + }), + entry("lines", new DisplayModifier() { @Override public void modifyDrawing(Paint paint, Canvas canvas) { canvas.drawLines(gLinePts, paint); } @Override protected int mask() { return SWEEP_STROKE_CAP_BIT; }; - }); - put("plusPoints", new DisplayModifier() { + }), + entry("plusPoints", new DisplayModifier() { @Override public void modifyDrawing(Paint paint, Canvas canvas) { canvas.drawPoints(gPts, paint); } - }); - put("text", new DisplayModifier() { + }), + entry("text", new DisplayModifier() { @Override public void modifyDrawing(Paint paint, Canvas canvas) { paint.setTextSize(36); canvas.drawText("TEXTTEST", 0, 50, paint); } - }); - put("shadowtext", new DisplayModifier() { + }), + entry("shadowtext", new DisplayModifier() { @Override public void modifyDrawing(Paint paint, Canvas canvas) { paint.setTextSize(36); paint.setShadowLayer(3.0f, 0.0f, 3.0f, 0xffff00ff); canvas.drawText("TEXTTEST", 0, 50, paint); } - }); - put("bitmapMesh", new DisplayModifier() { + }), + entry("bitmapMesh", new DisplayModifier() { @Override public void modifyDrawing(Paint paint, Canvas canvas) { canvas.drawBitmapMesh(ResourceModifiers.instance().mBitmap, 3, 3, ResourceModifiers.instance().mBitmapVertices, 0, null, 0, null); } - }); - put("arc", new DisplayModifier() { + }), + entry("arc", new DisplayModifier() { @Override public void modifyDrawing(Paint paint, Canvas canvas) { canvas.drawArc(gRect, 260, 285, false, paint); } @Override protected int mask() { return SWEEP_STROKE_CAP_BIT; }; - }); - put("arcFromCenter", new DisplayModifier() { + }), + entry("arcFromCenter", new DisplayModifier() { @Override public void modifyDrawing(Paint paint, Canvas canvas) { canvas.drawArc(gRect, 260, 285, true, paint); } @Override protected int mask() { return SWEEP_STROKE_JOIN_BIT; }; - }); - } - }); + }))); // WARNING: DON'T PUT MORE MAPS BELOW THIS - } - }; - private static LinkedHashMap getMapAtIndex(int index) { - for (LinkedHashMap map : gMaps.values()) { + private static Map getMapAtIndex(int index) { + for (Map map : gMaps.values()) { if (index == 0) { return map; } @@ -439,7 +410,7 @@ private static LinkedHashMap getMapAtIndex(int index) { private static boolean stepInternal(boolean forward) { int modifierMapIndex = gMaps.size() - 1; while (modifierMapIndex >= 0) { - LinkedHashMap map = getMapAtIndex(modifierMapIndex); + Map map = getMapAtIndex(modifierMapIndex); mIndices[modifierMapIndex] += (forward ? 1 : -1); if (mIndices[modifierMapIndex] >= 0 && mIndices[modifierMapIndex] < map.size()) { @@ -471,7 +442,7 @@ public static boolean stepBack() { private static boolean checkModificationStateMask() { int operatorMask = 0x0; int mapIndex = 0; - for (LinkedHashMap map : gMaps.values()) { + for (Map map : gMaps.values()) { int displayModifierIndex = mIndices[mapIndex]; for (Entry modifierEntry : map.entrySet()) { if (displayModifierIndex == 0) { @@ -488,7 +459,7 @@ private static boolean checkModificationStateMask() { public static void apply(Paint paint, Canvas canvas) { int mapIndex = 0; - for (LinkedHashMap map : gMaps.values()) { + for (Map map : gMaps.values()) { int displayModifierIndex = mIndices[mapIndex]; for (Entry modifierEntry : map.entrySet()) { if (displayModifierIndex == 0) { @@ -510,7 +481,7 @@ public static String[][] getStrings() { String[][] keys = new String[gMaps.size()][]; int i = 0; - for (LinkedHashMap map : gMaps.values()) { + for (Map map : gMaps.values()) { keys[i] = new String[map.size()]; int j = 0; for (String key : map.keySet()) { diff --git a/tests/HierarchyViewerTest/src/com/android/test/hierarchyviewer/ViewDumpParser.java b/tests/HierarchyViewerTest/src/com/android/test/hierarchyviewer/ViewDumpParser.java index 2ad0da98c409..8b9c02049351 100644 --- a/tests/HierarchyViewerTest/src/com/android/test/hierarchyviewer/ViewDumpParser.java +++ b/tests/HierarchyViewerTest/src/com/android/test/hierarchyviewer/ViewDumpParser.java @@ -58,7 +58,7 @@ public String getFirstView() { Object hash = getProperty(props, "__hash__"); if (name instanceof String && hash instanceof Integer) { - return String.format(Locale.US, "%s@%x", name, hash); + return String.format(Locale.US, "%s@%x", name, (Integer) hash); } else { return null; } diff --git a/tests/JankBench/app/src/main/java/com/android/benchmark/app/HomeActivity.java b/tests/JankBench/app/src/main/java/com/android/benchmark/app/HomeActivity.java index 4de51fb57308..43dc9de6c90a 100644 --- a/tests/JankBench/app/src/main/java/com/android/benchmark/app/HomeActivity.java +++ b/tests/JankBench/app/src/main/java/com/android/benchmark/app/HomeActivity.java @@ -140,9 +140,9 @@ public void onClick(View v) { handleNextBenchmark(); } + @SuppressWarnings("MissingSuperCall") // TODO: Fix me @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { - } private void handleNextBenchmark() { diff --git a/tests/JankBench/app/src/main/java/com/android/benchmark/app/RunLocalBenchmarksActivity.java b/tests/JankBench/app/src/main/java/com/android/benchmark/app/RunLocalBenchmarksActivity.java index c16efbda1830..d015a5695ec0 100644 --- a/tests/JankBench/app/src/main/java/com/android/benchmark/app/RunLocalBenchmarksActivity.java +++ b/tests/JankBench/app/src/main/java/com/android/benchmark/app/RunLocalBenchmarksActivity.java @@ -367,6 +367,7 @@ private void runBenchmarkForId(int id, int iteration) { } } + @SuppressWarnings("MissingSuperCall") // TODO: Fix me @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { switch (requestCode) { diff --git a/tests/MirrorSurfaceTest/src/com/google/android/test/mirrorsurface/MirrorSurfaceActivity.java b/tests/MirrorSurfaceTest/src/com/google/android/test/mirrorsurface/MirrorSurfaceActivity.java index 8afe8411a790..17fa210a1db6 100644 --- a/tests/MirrorSurfaceTest/src/com/google/android/test/mirrorsurface/MirrorSurfaceActivity.java +++ b/tests/MirrorSurfaceTest/src/com/google/android/test/mirrorsurface/MirrorSurfaceActivity.java @@ -295,8 +295,8 @@ private void createMirror(Rect displayFrame, float scale) { private void updateMirror(Rect displayFrame, float scale) { if (displayFrame.isEmpty()) { Rect bounds = mWindowBounds; - int defaultCropW = Math.round(bounds.width() / 2); - int defaultCropH = Math.round(bounds.height() / 2); + int defaultCropW = bounds.width() / 2; + int defaultCropH = bounds.height() / 2; displayFrame.set(0, 0, defaultCropW, defaultCropH); } diff --git a/tests/RenderThreadTest/src/com/example/renderthread/MainActivity.java b/tests/RenderThreadTest/src/com/example/renderthread/MainActivity.java index 241206d8919b..65b7549f22d1 100644 --- a/tests/RenderThreadTest/src/com/example/renderthread/MainActivity.java +++ b/tests/RenderThreadTest/src/com/example/renderthread/MainActivity.java @@ -24,18 +24,14 @@ public class MainActivity extends Activity implements OnItemClickListener { static final String KEY_NAME = "name"; static final String KEY_CLASS = "clazz"; - static Map make(String name) { - Map ret = new HashMap(); - ret.put(KEY_NAME, name); - return ret; - } - - @SuppressWarnings("serial") - static final ArrayList> SAMPLES = new ArrayList>() {{ + static final ArrayList> SAMPLES = new ArrayList<>(); + static { for (int i = 1; i < 25; i++) { - add(make("List Item: " + i)); + Map sample = new HashMap(); + sample.put(KEY_NAME, "List Item: " + i); + SAMPLES.add(sample); } - }}; + } Handler mHandler = new Handler(); diff --git a/tests/SmokeTestApps/src/com/android/smoketest/triggers/CrashyApp.java b/tests/SmokeTestApps/src/com/android/smoketest/triggers/CrashyApp.java index c11b0f3acf79..f85fb0f267d5 100644 --- a/tests/SmokeTestApps/src/com/android/smoketest/triggers/CrashyApp.java +++ b/tests/SmokeTestApps/src/com/android/smoketest/triggers/CrashyApp.java @@ -30,6 +30,7 @@ public void onCreate(Bundle savedInstanceState) { setContentView(tv); } + @SuppressWarnings("ReturnValueIgnored") @Override public void onResume() { ((String) null).length(); diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java index f924b2e9b932..ad068308d481 100644 --- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java +++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java @@ -637,8 +637,7 @@ public void testPackageChangeListener_packageRemoved() throws Exception { final BroadcastReceiver receiver = getPackageChangeReceiver(); verify(mMockContext).registerReceiver(any(), argThat(filter -> { - return filter.hasAction(Intent.ACTION_PACKAGE_REMOVED) - && filter.hasAction(Intent.ACTION_PACKAGE_REMOVED); + return filter.hasAction(Intent.ACTION_PACKAGE_REMOVED); }), any(), any()); receiver.onReceive(mMockContext, new Intent(Intent.ACTION_PACKAGE_REMOVED)); diff --git a/tools/aapt/Main.cpp b/tools/aapt/Main.cpp index 2f2ef92e72b4..6e05acf99c71 100644 --- a/tools/aapt/Main.cpp +++ b/tools/aapt/Main.cpp @@ -281,8 +281,8 @@ int main(int argc, char* const argv[]) int result = 1; // pessimistically assume an error. int tolerance = 0; - /* default to compression */ - bundle.setCompressionMethod(ZipEntry::kCompressDeflated); + /* default to 0 compression */ + bundle.setCompressionMethod(ZipEntry::kCompressStored); if (argc < 2) { wantUsage = true; diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp index 8d35eeec2a93..7f9457a1fb15 100644 --- a/tools/aapt2/ResourceParser.cpp +++ b/tools/aapt2/ResourceParser.cpp @@ -870,15 +870,14 @@ bool ResourceParser::ParseString(xml::XmlPullParser* parser, if (formatted && translatable) { if (!util::VerifyJavaStringFormat(*string_value->value)) { - DiagMessage msg(out_resource->source); - msg << "multiple substitutions specified in non-positional format; " - "did you mean to add the formatted=\"false\" attribute?"; if (options_.error_on_positional_arguments) { + DiagMessage msg(out_resource->source); + msg << "multiple substitutions specified in non-positional format; " + "did you mean to add the formatted=\"false\" attribute?"; + diag_->Error(msg); return false; } - - diag_->Warn(msg); } } diff --git a/wifi/tests/src/android/net/wifi/nl80211/WifiNl80211ManagerTest.java b/wifi/tests/src/android/net/wifi/nl80211/WifiNl80211ManagerTest.java index a750696628f9..50126226eb94 100644 --- a/wifi/tests/src/android/net/wifi/nl80211/WifiNl80211ManagerTest.java +++ b/wifi/tests/src/android/net/wifi/nl80211/WifiNl80211ManagerTest.java @@ -117,13 +117,12 @@ public class WifiNl80211ManagerTest { private static final byte[] TEST_PSK = new byte[]{'T', 'e', 's', 't'}; - private static final Set SCAN_FREQ_SET = - new HashSet() {{ - add(2410); - add(2450); - add(5050); - add(5200); - }}; + private static final Set SCAN_FREQ_SET = Set.of( + 2410, + 2450, + 5050, + 5200); + private static final String TEST_QUOTED_SSID_1 = "\"testSsid1\""; private static final String TEST_QUOTED_SSID_2 = "\"testSsid2\""; private static final int[] TEST_FREQUENCIES_1 = {}; @@ -131,13 +130,11 @@ public class WifiNl80211ManagerTest { private static final MacAddress TEST_RAW_MAC_BYTES = MacAddress.fromBytes( new byte[]{0x00, 0x01, 0x02, 0x03, 0x04, 0x05}); - private static final List SCAN_HIDDEN_NETWORK_SSID_LIST = - new ArrayList() {{ - add(LocalNativeUtil.byteArrayFromArrayList( - LocalNativeUtil.decodeSsid(TEST_QUOTED_SSID_1))); - add(LocalNativeUtil.byteArrayFromArrayList( - LocalNativeUtil.decodeSsid(TEST_QUOTED_SSID_2))); - }}; + private static final List SCAN_HIDDEN_NETWORK_SSID_LIST = List.of( + LocalNativeUtil.byteArrayFromArrayList( + LocalNativeUtil.decodeSsid(TEST_QUOTED_SSID_1)), + LocalNativeUtil.byteArrayFromArrayList( + LocalNativeUtil.decodeSsid(TEST_QUOTED_SSID_2))); private static final PnoSettings TEST_PNO_SETTINGS = new PnoSettings(); static {