From 3428bb6acf59d038f3119872065053b1b7e4cb65 Mon Sep 17 00:00:00 2001 From: Aryaman Date: Tue, 6 Oct 2020 07:01:18 +0000 Subject: [PATCH 01/26] base: overlays: Add Pebble icon shape --- packages/overlays/Android.mk | 1 + .../IconShapePebbleOverlay/Android.mk | 31 +++++++++++++++++++ .../AndroidManifest.xml | 27 ++++++++++++++++ .../res/values/config.xml | 28 +++++++++++++++++ .../res/values/strings.xml | 22 +++++++++++++ 5 files changed, 109 insertions(+) create mode 100644 packages/overlays/IconShapePebbleOverlay/Android.mk create mode 100644 packages/overlays/IconShapePebbleOverlay/AndroidManifest.xml create mode 100644 packages/overlays/IconShapePebbleOverlay/res/values/config.xml create mode 100644 packages/overlays/IconShapePebbleOverlay/res/values/strings.xml diff --git a/packages/overlays/Android.mk b/packages/overlays/Android.mk index 7b8d19952c5..cc52c91c596 100644 --- a/packages/overlays/Android.mk +++ b/packages/overlays/Android.mk @@ -43,6 +43,7 @@ LOCAL_REQUIRED_MODULES := \ IconPackRoundedSettingsOverlay \ IconPackRoundedSystemUIOverlay \ IconPackRoundedThemePickerUIOverlay \ + IconShapePebbleOverlay \ IconShapeRoundedRectOverlay \ IconShapeSquircleOverlay \ IconShapeTeardropOverlay \ diff --git a/packages/overlays/IconShapePebbleOverlay/Android.mk b/packages/overlays/IconShapePebbleOverlay/Android.mk new file mode 100644 index 00000000000..f0a730c81f8 --- /dev/null +++ b/packages/overlays/IconShapePebbleOverlay/Android.mk @@ -0,0 +1,31 @@ +# +# Copyright 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. +# + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_RRO_THEME := IconShapePebble + +LOCAL_PRODUCT_MODULE := true + +LOCAL_SRC_FILES := $(call all-subdir-java-files) + +LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res + +LOCAL_PACKAGE_NAME := IconShapePebbleOverlay +LOCAL_SDK_VERSION := current + +include $(BUILD_RRO_PACKAGE) diff --git a/packages/overlays/IconShapePebbleOverlay/AndroidManifest.xml b/packages/overlays/IconShapePebbleOverlay/AndroidManifest.xml new file mode 100644 index 00000000000..10f42e2e867 --- /dev/null +++ b/packages/overlays/IconShapePebbleOverlay/AndroidManifest.xml @@ -0,0 +1,27 @@ + + + + + + diff --git a/packages/overlays/IconShapePebbleOverlay/res/values/config.xml b/packages/overlays/IconShapePebbleOverlay/res/values/config.xml new file mode 100644 index 00000000000..33afdf785b5 --- /dev/null +++ b/packages/overlays/IconShapePebbleOverlay/res/values/config.xml @@ -0,0 +1,28 @@ + + + + + MM55,0 C25,0 0,25 0,50 0,78 28,100 55,100 85,100 100,85 100,58 100,30 86,0 55,0 Z + + false + + 8.0dip + + 16.0dip + diff --git a/packages/overlays/IconShapePebbleOverlay/res/values/strings.xml b/packages/overlays/IconShapePebbleOverlay/res/values/strings.xml new file mode 100644 index 00000000000..aacec0e52b5 --- /dev/null +++ b/packages/overlays/IconShapePebbleOverlay/res/values/strings.xml @@ -0,0 +1,22 @@ + + + + + Pebble + From a7dd354a7141db97d71cc8349e8426ae41574cc8 Mon Sep 17 00:00:00 2001 From: jhenrique09 Date: Sat, 10 Aug 2019 22:06:22 -0300 Subject: [PATCH 02/26] base: Port extended screenshot function from OOS Change-Id: I856597056cb56d479a7a2c47dfdfc199da4302b8 --- Android.bp | 6 + .../android/app/SystemServiceRegistry.java | 9 + core/java/android/content/Context.java | 6 + core/java/android/provider/Settings.java | 9 +- core/java/android/view/IWindowManager.aidl | 7 + core/java/android/view/InputDevice.java | 7 + core/java/android/view/View.java | 21 +- core/java/android/view/WindowManager.java | 6 + core/java/android/widget/ScrollView.java | 5 + core/java/android/widget/Toast.java | 8 +- .../custom/longshot/ILongScreenshot.aidl | 37 ++++ .../longshot/ILongScreenshotCallback.aidl | 25 +++ .../longshot/ILongScreenshotListener.aidl | 23 +++ .../longshot/ILongScreenshotManager.aidl | 43 ++++ .../longshot/LongScreenshotManager.java | 125 +++++++++++ .../LongScreenshotManagerService.java | 195 ++++++++++++++++++ .../longshot/LongScreenshotService.java | 38 ++++ .../longshot/injector/ScrollViewInjector.java | 22 ++ .../longshot/injector/ViewInjector.java | 181 ++++++++++++++++ .../internal/util/ScreenshotHelper.java | 121 +++++++++++ .../internal/view/IInputMethodManager.aidl | 3 + .../src/com/android/settingslib/Utils.java | 2 + packages/SystemUI/AndroidManifest.xml | 8 + .../globalactions/GlobalActionsDialog.java | 6 +- .../systemui/screenshot/GlobalScreenshot.java | 2 +- ...ScreenshotServiceCaptureErrorReceiver.java | 40 ++++ .../InputMethodManagerService.java | 32 +++ .../MultiClientInputMethodManagerService.java | 5 + .../server/policy/PhoneWindowManager.java | 29 ++- .../server/policy/WindowManagerPolicy.java | 7 + .../com/android/server/wm/DisplayPolicy.java | 46 ++++- .../server/wm/WindowManagerInternal.java | 4 + .../server/wm/WindowManagerService.java | 24 +++ .../java/com/android/server/SystemServer.java | 5 + 34 files changed, 1098 insertions(+), 9 deletions(-) create mode 100644 core/java/com/android/internal/custom/longshot/ILongScreenshot.aidl create mode 100644 core/java/com/android/internal/custom/longshot/ILongScreenshotCallback.aidl create mode 100644 core/java/com/android/internal/custom/longshot/ILongScreenshotListener.aidl create mode 100644 core/java/com/android/internal/custom/longshot/ILongScreenshotManager.aidl create mode 100644 core/java/com/android/internal/custom/longshot/LongScreenshotManager.java create mode 100644 core/java/com/android/internal/custom/longshot/LongScreenshotManagerService.java create mode 100644 core/java/com/android/internal/custom/longshot/LongScreenshotService.java create mode 100644 core/java/com/android/internal/custom/longshot/injector/ScrollViewInjector.java create mode 100644 core/java/com/android/internal/custom/longshot/injector/ViewInjector.java create mode 100644 packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotServiceCaptureErrorReceiver.java diff --git a/Android.bp b/Android.bp index 33f803d040a..b83893c7328 100755 --- a/Android.bp +++ b/Android.bp @@ -695,6 +695,12 @@ java_defaults { ":platform-properties", ":framework-statslog-gen", + + // Long screenshot + "core/java/com/android/internal/custom/longshot/ILongScreenshot.aidl", + "core/java/com/android/internal/custom/longshot/ILongScreenshotCallback.aidl", + "core/java/com/android/internal/custom/longshot/ILongScreenshotListener.aidl", + "core/java/com/android/internal/custom/longshot/ILongScreenshotManager.aidl", ], aidl: { diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index 136e932a3eb..95254cdccba 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -193,6 +193,7 @@ import com.android.internal.app.IBatteryStats; import com.android.internal.app.ISoundTriggerService; import com.android.internal.appwidget.IAppWidgetService; +import com.android.internal.custom.longshot.LongScreenshotManager; import com.android.internal.net.INetworkWatchlistManager; import com.android.internal.os.IDropBoxManagerService; import com.android.internal.policy.PhoneLayoutInflater; @@ -1300,6 +1301,14 @@ public DynamicSystemManager createService(ContextImpl ctx) return new DynamicSystemManager( IDynamicSystemService.Stub.asInterface(b)); }}); + + registerService(Context.LONGSCREENSHOT_SERVICE, LongScreenshotManager.class, + new CachedServiceFetcher() { + @Override + public LongScreenshotManager createService(ContextImpl ctx) + throws ServiceNotFoundException { + return LongScreenshotManager.getInstance(); + }}); //CHECKSTYLE:ON IndentationCheck } diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 414cc39f531..7056283c697 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -4685,6 +4685,12 @@ public abstract boolean startInstrumentation(@NonNull ComponentName className, */ public static final String DYNAMIC_SYSTEM_SERVICE = "dynamic_system"; + /** + * Long screenshot + * @hide + */ + public static final String LONGSCREENSHOT_SERVICE = "longshot"; + /** * Determine whether the given permission is allowed for a particular * process and user ID running in the system. diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index cf9f419ba99..96ed81f9eb2 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -4535,13 +4535,20 @@ public boolean validate(@Nullable String value) { /** @hide */ private static final Validator DOUBLE_TAP_SLEEP_LOCKSCREEN_VALIDATOR = BOOLEAN_VALIDATOR; - + /** * Disable Screenshot shutter sound * @hide */ public static final String SCREENSHOT_SHUTTER_SOUND = "screenshot_shutter_sound"; + /** + * Screenshod sound enable, This is the noise made when taking a screesnhot + * Defaults to 1 - sounds enabled + * @hide + */ + public static final String SCREENSHOT_SOUND = "screenshot_sound"; + /** @hide */ private static final Validator SCREENSHOT_SHUTTER_SOUND_VALIDATOR = BOOLEAN_VALIDATOR; diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index ce39d9d46c8..f7f4b05d1ae 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -644,4 +644,11 @@ interface IWindowManager * native InputManager before proceeding with tests. */ void syncInputTransactions(); + + /** + * Long screenshot + * @hide + */ + void takeOPScreenshot(int type); + void stopLongshotConnection(); } diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java index e723f91887c..9c4dffa3d8b 100644 --- a/core/java/android/view/InputDevice.java +++ b/core/java/android/view/InputDevice.java @@ -138,6 +138,13 @@ public final class InputDevice implements Parcelable { */ public static final int SOURCE_CLASS_JOYSTICK = 0x00000010; + /** + * The input source is emulated by Longshot + * + * @hide + */ + public static final int SOURCE_CLASS_LONGSHOT = 0x10000000; + /** * The input source is unknown. */ diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index cdea23ab525..a5cb8f49315 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -137,6 +137,7 @@ import android.widget.ScrollBarDrawable; import com.android.internal.R; +import com.android.internal.custom.longshot.injector.ViewInjector; import com.android.internal.view.TooltipPopup; import com.android.internal.view.menu.MenuBuilder; import com.android.internal.widget.ScrollBarUtils; @@ -15301,6 +15302,7 @@ public boolean onTouchEvent(MotionEvent event) { || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) || (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE; + ViewInjector.View.isInjection = event.isFromSource(InputDevice.SOURCE_CLASS_LONGSHOT); if ((viewFlags & ENABLED_MASK) == DISABLED) { if (action == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) { setPressed(false); @@ -15316,6 +15318,22 @@ public boolean onTouchEvent(MotionEvent event) { } } + if (action == 0 && isInScrollingContainer() && ViewInjector.View.isInjection) { + ViewParent targetView = getParent(); + while (true) { + if (targetView == null || !(targetView instanceof ViewGroup)) { + break; + } else if (((ViewGroup) targetView).shouldDelayChildPressedState()) { + int[] position = new int[2]; + ((ViewGroup) targetView).getLocationOnScreen(position); + ViewInjector.View.setScrolledViewTop(mContext, position[1]); + break; + } else { + targetView = targetView.getParent(); + } + } + } + if (clickable || (viewFlags & TOOLTIP) == TOOLTIP) { switch (action) { case MotionEvent.ACTION_UP: @@ -17986,7 +18004,7 @@ protected boolean awakenScrollBars(int startDelay) { protected boolean awakenScrollBars(int startDelay, boolean invalidate) { final ScrollabilityCache scrollCache = mScrollCache; - if (scrollCache == null || !scrollCache.fadeScrollBars) { + if (ViewInjector.View.onAwakenScrollBars(mContext) || scrollCache == null || !scrollCache.fadeScrollBars) { return false; } @@ -26375,6 +26393,7 @@ protected boolean overScrollBy(int deltaX, int deltaY, } onOverScrolled(newScrollX, newScrollY, clampedX, clampedY); + ViewInjector.View.onOverScrolled(mContext, clampedY); return clampedX || clampedY; } diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 4798d19e946..62c8a96971b 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -1110,6 +1110,12 @@ public static class LayoutParams extends ViewGroup.LayoutParams implements Parce */ public static final int TYPE_APPLICATION_OVERLAY = FIRST_SYSTEM_WINDOW + 38; + /** + * Window type: Long screenshot overlay + * @hide + */ + public static final int TYPE_SYSTEM_LONGSHOT = FIRST_SYSTEM_WINDOW + 39; + /** * End of types of system windows. */ diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java index 6c9fef29db6..19258934ab2 100644 --- a/core/java/android/widget/ScrollView.java +++ b/core/java/android/widget/ScrollView.java @@ -49,6 +49,7 @@ import android.view.inspector.InspectableProperty; import com.android.internal.R; +import com.android.internal.custom.longshot.injector.ScrollViewInjector; import java.util.List; @@ -728,6 +729,8 @@ public boolean onTouchEvent(MotionEvent ev) { } vtev.offsetLocation(0, mNestedYOffset); + ScrollViewInjector.ScrollView.isInjection = ev.isFromSource(InputDevice.SOURCE_CLASS_LONGSHOT); + switch (actionMasked) { case MotionEvent.ACTION_DOWN: { if (getChildCount() == 0) { @@ -816,12 +819,14 @@ public boolean onTouchEvent(MotionEvent ev) { if (!mEdgeGlowBottom.isFinished()) { mEdgeGlowBottom.onRelease(); } + ScrollViewInjector.ScrollView.onOverScrolled(mContext, true); } else if (pulledToY > range) { mEdgeGlowBottom.onPull((float) deltaY / getHeight(), 1.f - ev.getX(activePointerIndex) / getWidth()); if (!mEdgeGlowTop.isFinished()) { mEdgeGlowTop.onRelease(); } + ScrollViewInjector.ScrollView.onOverScrolled(mContext, true); } if (shouldDisplayEdgeEffects() && (!mEdgeGlowTop.isFinished() || !mEdgeGlowBottom.isFinished())) { diff --git a/core/java/android/widget/Toast.java b/core/java/android/widget/Toast.java index d037337d454..7f16ed7bbe9 100644 --- a/core/java/android/widget/Toast.java +++ b/core/java/android/widget/Toast.java @@ -42,6 +42,8 @@ import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; +import com.android.internal.custom.longshot.LongScreenshotManagerService; + import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -392,7 +394,11 @@ private static class TN extends ITransientNotification.Stub { params.width = WindowManager.LayoutParams.WRAP_CONTENT; params.format = PixelFormat.TRANSLUCENT; params.windowAnimations = com.android.internal.R.style.Animation_Toast; - params.type = WindowManager.LayoutParams.TYPE_TOAST; + if (LongScreenshotManagerService.PACKAGENAME_LONGSHOT.equals(packageName)) { + params.type = WindowManager.LayoutParams.TYPE_SYSTEM_LONGSHOT; + } else { + params.type = WindowManager.LayoutParams.TYPE_TOAST; + } params.setTitle("Toast"); params.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE diff --git a/core/java/com/android/internal/custom/longshot/ILongScreenshot.aidl b/core/java/com/android/internal/custom/longshot/ILongScreenshot.aidl new file mode 100644 index 00000000000..379d0202d5a --- /dev/null +++ b/core/java/com/android/internal/custom/longshot/ILongScreenshot.aidl @@ -0,0 +1,37 @@ +/** + * Copyright (C) 2019 The PixelExperience Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.custom.longshot; + +import com.android.internal.custom.longshot.ILongScreenshotCallback; + +/** @hide */ +interface ILongScreenshot { + + boolean isHandleState(); + + boolean isMoveState(); + + void notifyScroll(boolean isOverScroll); + + void notifyScrollViewTop(int viewTop); + + void onUnscrollableView(); + + void start(in ILongScreenshotCallback callback); + + void stopLongshot(); +} diff --git a/core/java/com/android/internal/custom/longshot/ILongScreenshotCallback.aidl b/core/java/com/android/internal/custom/longshot/ILongScreenshotCallback.aidl new file mode 100644 index 00000000000..f9060dc3b0a --- /dev/null +++ b/core/java/com/android/internal/custom/longshot/ILongScreenshotCallback.aidl @@ -0,0 +1,25 @@ +/** + * Copyright (C) 2019 The PixelExperience Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.custom.longshot; + +/** @hide */ +interface ILongScreenshotCallback { + + void notifyMove(); + + void stop(); +} diff --git a/core/java/com/android/internal/custom/longshot/ILongScreenshotListener.aidl b/core/java/com/android/internal/custom/longshot/ILongScreenshotListener.aidl new file mode 100644 index 00000000000..0e399b3c62f --- /dev/null +++ b/core/java/com/android/internal/custom/longshot/ILongScreenshotListener.aidl @@ -0,0 +1,23 @@ +/** + * Copyright (C) 2019 The PixelExperience Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.custom.longshot; + +/** @hide */ +interface ILongScreenshotListener { + + void onMove(); +} diff --git a/core/java/com/android/internal/custom/longshot/ILongScreenshotManager.aidl b/core/java/com/android/internal/custom/longshot/ILongScreenshotManager.aidl new file mode 100644 index 00000000000..59aacd8a3b1 --- /dev/null +++ b/core/java/com/android/internal/custom/longshot/ILongScreenshotManager.aidl @@ -0,0 +1,43 @@ +/** + * Copyright (C) 2019 The PixelExperience Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.custom.longshot; + +import com.android.internal.custom.longshot.ILongScreenshotListener; + +/** @hide */ +interface ILongScreenshotManager { + + boolean isLongshotHandleState(); + + boolean isLongshotMode(); + + boolean isLongshotMoveState(); + + void notifyLongshotScroll(boolean isOverScroll); + + void notifyScrollViewTop(int viewTop); + + void onUnscrollableView(); + + void registerLongshotListener(in ILongScreenshotListener listener); + + void stopLongshot(); + + void takeLongshot(boolean statusBarVisible, boolean navBarVisible); + + void unregisterLongshotListener(in ILongScreenshotListener listener); +} diff --git a/core/java/com/android/internal/custom/longshot/LongScreenshotManager.java b/core/java/com/android/internal/custom/longshot/LongScreenshotManager.java new file mode 100644 index 00000000000..511346027ec --- /dev/null +++ b/core/java/com/android/internal/custom/longshot/LongScreenshotManager.java @@ -0,0 +1,125 @@ +package com.android.internal.custom.longshot; + +import android.content.Context; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.util.Log; + +public final class LongScreenshotManager { + public static final String NAVIGATIONBAR_VISIBLE = "navigationbar_visible"; + public static final String STATUSBAR_VISIBLE = "statusbar_visible"; + private static final String TAG = "Longshot.Manager"; + private static LongScreenshotManager sInstance = null; + private final ILongScreenshotManager mService = ILongScreenshotManager.Stub.asInterface(ServiceManager.getService(Context.LONGSCREENSHOT_SERVICE)); + + private LongScreenshotManager() { + } + + public static LongScreenshotManager getInstance() { + LongScreenshotManager longScreenshotManager; + synchronized (LongScreenshotManager.class) { + if (sInstance == null || sInstance.mService == null) { + sInstance = new LongScreenshotManager(); + } + longScreenshotManager = sInstance; + } + return longScreenshotManager; + } + + public static LongScreenshotManager peekInstance() { + return sInstance; + } + + public void takeLongshot(boolean statusBarVisible, boolean navBarVisible) { + try { + if (mService != null) { + mService.takeLongshot(statusBarVisible, navBarVisible); + } + } catch (RemoteException e) { + Log.w(TAG, "Remote exception in takeLongshot: ", e); + } + } + + public void registerLongshotListener(ILongScreenshotListener listener) { + try { + if (mService != null) { + mService.registerLongshotListener(listener); + } + } catch (RemoteException e) { + Log.w(TAG, "Remote exception in registerLongshotListener: ", e); + } + } + + public void unregisterLongshotListener(ILongScreenshotListener listener) { + try { + if (mService != null) { + mService.unregisterLongshotListener(listener); + } + } catch (RemoteException e) { + Log.w(TAG, "Remote exception in unregisterLongshotListener: ", e); + } + } + + public void notifyLongshotScroll(boolean isOverScroll) { + try { + if (mService != null) { + mService.notifyLongshotScroll(isOverScroll); + } + } catch (RemoteException e) { + Log.w(TAG, "Remote exception in notifyLongshotScroll: ", e); + } + } + + public boolean isLongshotMoveState() { + try { + if (mService != null) { + return mService.isLongshotMoveState(); + } + } catch (RemoteException e) { + Log.w(TAG, "Remote exception in isLongshotMoveState: ", e); + } + return false; + } + + public boolean isLongshotHandleState() { + try { + if (mService != null) { + return mService.isLongshotHandleState(); + } + } catch (RemoteException e) { + Log.w(TAG, "Remote exception in isLongshotHandleState: ", e); + } + return false; + } + + public boolean isLongshotMode() { + try { + if (mService != null) { + return mService.isLongshotMode(); + } + } catch (RemoteException e) { + Log.w(TAG, "Remote exception in isLongshotMode: ", e); + } + return false; + } + + public void notifyScrollViewTop(int viewTop) { + try { + if (mService != null) { + mService.notifyScrollViewTop(viewTop); + } + } catch (RemoteException e) { + Log.w(TAG, "Remote exception in notifyScrollViewTop: ", e); + } + } + + public void onUnscrollableView() { + try { + if (mService != null) { + mService.onUnscrollableView(); + } + } catch (RemoteException e) { + Log.w(TAG, "Remote exception in onUnscrollableView: ", e); + } + } +} diff --git a/core/java/com/android/internal/custom/longshot/LongScreenshotManagerService.java b/core/java/com/android/internal/custom/longshot/LongScreenshotManagerService.java new file mode 100644 index 00000000000..8b464f4801a --- /dev/null +++ b/core/java/com/android/internal/custom/longshot/LongScreenshotManagerService.java @@ -0,0 +1,195 @@ +package com.android.internal.custom.longshot; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.IBinder; +import android.os.RemoteException; +import android.os.UserHandle; +import android.util.Log; +import android.view.WindowManagerGlobal; +import java.util.ArrayList; +import java.util.List; + +public class LongScreenshotManagerService extends ILongScreenshotManager.Stub { + public static final String PACKAGENAME_LONGSHOT = "com.android.screenshot"; + public static final ComponentName TAKE_SCREENSHOT_COMPONENT = new ComponentName(PACKAGENAME_LONGSHOT, PACKAGENAME_LONGSHOT + ".TakeScreenshotService"); + private static final ComponentName COMPONENT_LONGSHOT = new ComponentName(PACKAGENAME_LONGSHOT, PACKAGENAME_LONGSHOT + ".LongshotService"); + private static final String TAG = "Longshot.ManagerService"; + private static LongScreenshotManagerService sInstance = null; + public Context mContext = null; + private LongshotConnection mLongshot = new LongshotConnection(); + + private class LongshotConnection extends ILongScreenshotCallback.Stub implements ServiceConnection { + private List mListeners; + public ILongScreenshot mService; + + private LongshotConnection() { + mService = null; + mListeners = new ArrayList(); + } + + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + mService = ILongScreenshot.Stub.asInterface(service); + try { + mService.start(this); + } catch (NullPointerException ignored) { + } catch (RemoteException e) { + Log.w(TAG, "Remote exception in onServiceConnected: ", e); + } + } + + @Override + public void onServiceDisconnected(ComponentName name) { + stop(); + } + + @Override + public void stop() { + mContext.unbindService(this); + mService = null; + try { + WindowManagerGlobal.getWindowManagerService().stopLongshotConnection(); + } catch (RemoteException e) { + Log.w(TAG, "Remote exception in stop: ", e); + } + } + + @Override + public void notifyMove() { + synchronized (mListeners) { + for (ILongScreenshotListener listener : mListeners) { + try { + listener.onMove(); + } catch (RemoteException e) { + Log.w(TAG, "Remote exception in notifyMove: ", e); + } + } + } + } + + public void registerListener(ILongScreenshotListener listener) { + synchronized (mListeners) { + mListeners.add(listener); + } + } + + public void unregisterListener(ILongScreenshotListener listener) { + synchronized (mListeners) { + mListeners.remove(listener); + } + } + } + + private LongScreenshotManagerService(Context context) { + mContext = context; + } + + public static LongScreenshotManagerService getInstance(Context context) { + if (sInstance == null) { + sInstance = new LongScreenshotManagerService(context); + } + return sInstance; + } + + @Override + public void takeLongshot(boolean statusBarVisible, boolean navBarVisible) { + stopLongshot(); + bindService(createLongshotIntent(statusBarVisible, navBarVisible), mLongshot, 1); + } + + @Override + public void registerLongshotListener(ILongScreenshotListener listener) { + mLongshot.registerListener(listener); + } + + @Override + public void unregisterLongshotListener(ILongScreenshotListener listener) { + mLongshot.unregisterListener(listener); + } + + @Override + public void notifyLongshotScroll(boolean isOverScroll) { + try { + mLongshot.mService.notifyScroll(isOverScroll); + } catch (NullPointerException ignored) { + } catch (RemoteException e) { + Log.w(TAG, "Remote exception in notifyLongshotScroll: ", e); + } + } + + @Override + public boolean isLongshotMoveState() { + try { + return mLongshot.mService.isMoveState(); + } catch (NullPointerException ignored) { + } catch (RemoteException e) { + Log.w(TAG, "Remote exception in isLongshotMoveState: ", e); + } + return false; + } + + @Override + public boolean isLongshotHandleState() { + try { + return mLongshot.mService.isHandleState(); + } catch (NullPointerException ignored) { + } catch (RemoteException e) { + Log.w(TAG, "Remote exception in isHandleState: ", e); + } + return false; + } + + @Override + public void notifyScrollViewTop(int viewTop) { + try { + mLongshot.mService.notifyScrollViewTop(viewTop); + } catch (NullPointerException ignored) { + } catch (RemoteException e) { + Log.w(TAG, "Remote exception in notifyScrollViewTop: ", e); + } + } + + @Override + public void onUnscrollableView() { + try { + mLongshot.mService.onUnscrollableView(); + } catch (NullPointerException ignored) { + } catch (RemoteException e) { + Log.w(TAG, "Remote exception in onUnscrollableView: ", e); + } + } + + @Override + public boolean isLongshotMode() { + return mLongshot.mService != null; + } + + @Override + public void stopLongshot() { + if (mLongshot.mService != null) { + try { + mLongshot.mService.stopLongshot(); + } catch (RemoteException e) { + Log.w(TAG, "Remote exception in stopLongshot: ", e); + } + } + } + + private Intent createIntent(ComponentName component) { + return new Intent().setComponent(component); + } + + private Intent createLongshotIntent(boolean statusBarVisible, boolean navBarVisible) { + return createIntent(COMPONENT_LONGSHOT).putExtra(LongScreenshotManager.STATUSBAR_VISIBLE, statusBarVisible).putExtra(LongScreenshotManager.NAVIGATIONBAR_VISIBLE, navBarVisible); + } + + private boolean bindService(Intent service, ServiceConnection conn, int flags) { + if (service != null && conn != null) { + return mContext.bindServiceAsUser(service, conn, flags, UserHandle.CURRENT); + } + return false; + } +} diff --git a/core/java/com/android/internal/custom/longshot/LongScreenshotService.java b/core/java/com/android/internal/custom/longshot/LongScreenshotService.java new file mode 100644 index 00000000000..fe68f720d45 --- /dev/null +++ b/core/java/com/android/internal/custom/longshot/LongScreenshotService.java @@ -0,0 +1,38 @@ +package com.android.internal.custom.longshot; + +import android.content.Context; + +public abstract class LongScreenshotService extends ILongScreenshot.Stub { + private static final String TAG = "Longshot.Service"; + protected Context mContext = null; + protected boolean mNavBarVisible = false; + protected boolean mStatusBarVisible = false; + + public LongScreenshotService(Context context, boolean statusBarVisible, boolean navBarVisible) { + mContext = context; + mStatusBarVisible = statusBarVisible; + mNavBarVisible = navBarVisible; + } + + @Override + public void start(ILongScreenshotCallback callback) { + } + + @Override + public void notifyScroll(boolean isOverScroll) { + } + + @Override + public boolean isMoveState() { + return false; + } + + @Override + public boolean isHandleState() { + return false; + } + + @Override + public void stopLongshot() { + } +} diff --git a/core/java/com/android/internal/custom/longshot/injector/ScrollViewInjector.java b/core/java/com/android/internal/custom/longshot/injector/ScrollViewInjector.java new file mode 100644 index 00000000000..dea5cf66b51 --- /dev/null +++ b/core/java/com/android/internal/custom/longshot/injector/ScrollViewInjector.java @@ -0,0 +1,22 @@ +package com.android.internal.custom.longshot.injector; + +import android.content.Context; + +import com.android.internal.custom.longshot.LongScreenshotManager; + +public class ScrollViewInjector { + + public static class ScrollView { + private static final String TAG = "ScrollViewInjector"; + public static boolean isInjection = false; + + public static void onOverScrolled(Context context, boolean isOverScroll) { + if (isInjection) { + LongScreenshotManager sm = (LongScreenshotManager) context.getSystemService(Context.LONGSCREENSHOT_SERVICE); + if (sm != null && sm.isLongshotMoveState()) { + sm.notifyLongshotScroll(isOverScroll); + } + } + } + } +} diff --git a/core/java/com/android/internal/custom/longshot/injector/ViewInjector.java b/core/java/com/android/internal/custom/longshot/injector/ViewInjector.java new file mode 100644 index 00000000000..cf3a7e657cc --- /dev/null +++ b/core/java/com/android/internal/custom/longshot/injector/ViewInjector.java @@ -0,0 +1,181 @@ +package com.android.internal.custom.longshot.injector; + +import android.content.Context; + +import com.android.internal.custom.longshot.LongScreenshotManager; + +import java.util.ArrayList; +import java.util.List; + +public class ViewInjector { + + public static class View { + + private static final List ELEMENTS_NOOVERSCROLL = new ArrayList(); + private static final List ELEMENTS_NOSCROLL = new ArrayList(); + private static final List ELEMENTS_OVERSCROLL = new ArrayList(); + private static final List ELEMENTS_SCROLL = new ArrayList(); + private static final String TAG = "ViewInjector"; + public static boolean isInjection = false; + + private enum Element { + SCROLL(5, "AbsListView.trackMotionScroll"), + QQSCROLL(7, "tencent.widget.AbsListView.onTouchEvent"), + MMAWAKEN12(12, "tencent.mm.ui.base.MMPullDownView.dispatchTouchEvent"), + MMAWAKEN14(14, "tencent.mm.ui.base.MMPullDownView.dispatchTouchEvent"), + MMAWAKEN15(15, "tencent.mm.ui.base.MMPullDownView.dispatchTouchEvent"), + OVERSCROLL(5, "AbsListView.onOverScrolled"), + CONTENTSCROLL(4, "ContentView.onScrollChanged"), + MMCHANGE9(9, "tencent.mm.ui.base.MMPullDownView.dispatchTouchEvent"), + MMCHANGE12(12, "tencent.mm.ui.base.MMPullDownView.dispatchTouchEvent"), + MMCHANGE14(14, "tencent.mm.ui.base.MMPullDownView.dispatchTouchEvent"), + MMCHANGE15(15, "tencent.mm.ui.base.MMPullDownView.dispatchTouchEvent"), + BROWSERSCROLL(14, "oppo.browser.navigation.widget.NavigationView.dispatchTouchEvent"), + QZONESCROLL(8, "qzone.widget.QZonePullToRefreshListView.onScrollChanged"), + WEBSCROLL(16, "WebView$PrivateAccess.overScrollBy"), + LISTOVERSCROLL(6, "AbsListView.onTouchEvent"), + WEBOVERSCROLL(5, "WebView$PrivateAccess.overScrollBy"), + BROWSEROVERSCROLL(11, "oppo.browser.navigation.widget.NavigationView.dispatchTouchEvent"); + + private String mName; + private int mPosition; + + private Element(int position, String name) { + this.mName = null; + this.mPosition = -1; + this.mPosition = position; + this.mName = name; + } + + public int getPosition() { + return this.mPosition; + } + + public String getName() { + return this.mName; + } + + public String getNameString() { + StringBuilder sb = new StringBuilder(); + sb.append("."); + sb.append(getName()); + sb.append("("); + return sb.toString(); + } + } + + public static void onUnscrollableView(Context context) { + if (isInjection) { + LongScreenshotManager sm = (LongScreenshotManager) context.getSystemService(Context.LONGSCREENSHOT_SERVICE); + if (sm != null) { + sm.onUnscrollableView(); + } + } + } + + public static void setScrolledViewTop(Context context, int top) { + if (isInjection) { + LongScreenshotManager sm = (LongScreenshotManager) context.getSystemService(Context.LONGSCREENSHOT_SERVICE); + if (sm != null) { + sm.notifyScrollViewTop(top); + } + } + } + + public static void onOverScrolled(Context context, boolean isOverScroll) { + if (isInjection) { + LongScreenshotManager sm = (LongScreenshotManager) context.getSystemService(Context.LONGSCREENSHOT_SERVICE); + if (sm != null && sm.isLongshotMoveState()) { + StackTraceElement[] stacks = Thread.currentThread().getStackTrace(); + ELEMENTS_OVERSCROLL.add(Element.LISTOVERSCROLL); + ELEMENTS_NOOVERSCROLL.add(Element.WEBOVERSCROLL); + ELEMENTS_NOOVERSCROLL.add(Element.BROWSERSCROLL); + ELEMENTS_NOOVERSCROLL.add(Element.BROWSEROVERSCROLL); + if (!isElement(stacks, ELEMENTS_NOOVERSCROLL)) { + if (isElement(stacks, ELEMENTS_OVERSCROLL)) { + sm.notifyLongshotScroll(true); + } else { + sm.notifyLongshotScroll(false); + } + } + clearElements(); + } + } + } + + public static void onScrollChanged(Context context, boolean canScrollVertically) { + if (isInjection) { + LongScreenshotManager sm = (LongScreenshotManager) context.getSystemService(Context.LONGSCREENSHOT_SERVICE); + if (sm != null && sm.isLongshotMoveState()) { + StackTraceElement[] stacks = Thread.currentThread().getStackTrace(); + ELEMENTS_NOSCROLL.add(Element.MMCHANGE9); + ELEMENTS_NOSCROLL.add(Element.MMCHANGE12); + ELEMENTS_NOSCROLL.add(Element.MMCHANGE14); + ELEMENTS_NOSCROLL.add(Element.MMCHANGE15); + ELEMENTS_NOSCROLL.add(Element.CONTENTSCROLL); + ELEMENTS_NOSCROLL.add(Element.BROWSERSCROLL); + ELEMENTS_NOSCROLL.add(Element.QZONESCROLL); + ELEMENTS_NOSCROLL.add(Element.WEBSCROLL); + if (!isElement(stacks, ELEMENTS_NOSCROLL)) { + if (!canScrollVertically) { + sm.notifyLongshotScroll(true); + } else { + sm.notifyLongshotScroll(false); + } + } + clearElements(); + } + } + } + + public static boolean onAwakenScrollBars(Context context) { + if (!isInjection) { + return false; + } + boolean result = false; + LongScreenshotManager sm = (LongScreenshotManager) context.getSystemService(Context.LONGSCREENSHOT_SERVICE); + if (sm != null) { + result = sm.isLongshotMoveState(); + if (result) { + StackTraceElement[] stacks = Thread.currentThread().getStackTrace(); + ELEMENTS_OVERSCROLL.add(Element.OVERSCROLL); + ELEMENTS_NOSCROLL.add(Element.MMAWAKEN12); + ELEMENTS_NOSCROLL.add(Element.MMAWAKEN14); + ELEMENTS_NOSCROLL.add(Element.MMAWAKEN15); + ELEMENTS_SCROLL.add(Element.QQSCROLL); + ELEMENTS_SCROLL.add(Element.SCROLL); + if (!isElement(stacks, ELEMENTS_NOSCROLL)) { + if (isElement(stacks, ELEMENTS_OVERSCROLL)) { + sm.notifyLongshotScroll(true); + } else if (isElement(stacks, ELEMENTS_SCROLL)) { + sm.notifyLongshotScroll(false); + } + } + clearElements(); + } + } + return result; + } + + private static boolean isElement(StackTraceElement[] stacks, List elements) { + boolean result = false; + for (Element element : elements) { + int pos = element.getPosition(); + if (stacks.length > pos) { + result = stacks[pos].toString().contains(element.getNameString()); + if (result) { + break; + } + } + } + return result; + } + + private static void clearElements() { + ELEMENTS_SCROLL.clear(); + ELEMENTS_NOSCROLL.clear(); + ELEMENTS_OVERSCROLL.clear(); + ELEMENTS_NOOVERSCROLL.clear(); + } + } +} diff --git a/core/java/com/android/internal/util/ScreenshotHelper.java b/core/java/com/android/internal/util/ScreenshotHelper.java index 68f32e5f4b0..7c2c8afd9cb 100644 --- a/core/java/com/android/internal/util/ScreenshotHelper.java +++ b/core/java/com/android/internal/util/ScreenshotHelper.java @@ -9,6 +9,7 @@ import android.content.Intent; import android.content.ServiceConnection; import android.net.Uri; +import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Message; @@ -16,6 +17,9 @@ import android.os.RemoteException; import android.os.UserHandle; import android.util.Log; +import android.view.WindowManager; + +import com.android.internal.custom.longshot.LongScreenshotManagerService; import java.util.function.Consumer; @@ -27,6 +31,8 @@ public class ScreenshotHelper { "com.android.systemui.screenshot.TakeScreenshotService"; private static final String SYSUI_SCREENSHOT_ERROR_RECEIVER = "com.android.systemui.screenshot.ScreenshotServiceErrorReceiver"; + private static final String SYSUI_SCREENSHOT_CAPTURE_ERROR_RECEIVER = + "com.android.systemui.screenshot.ScreenshotServiceCaptureErrorReceiver"; // Time until we give up on the screenshot & show an error instead. private final int SCREENSHOT_TIMEOUT_MS = 10000; @@ -35,6 +41,19 @@ public class ScreenshotHelper { private ServiceConnection mScreenshotConnection = null; private final Context mContext; + private Handler mHandler; + final Runnable mLongshotTimeout = new Runnable() { + public void run() { + synchronized (mScreenshotLock) { + if (mScreenshotConnection != null) { + mContext.unbindService(mScreenshotConnection); + mScreenshotConnection = null; + notifyScreenshotError(); + } + } + } + }; + public ScreenshotHelper(Context context) { mContext = context; } @@ -197,4 +216,106 @@ private void notifyScreenshotError() { mContext.sendBroadcastAsUser(errorIntent, UserHandle.CURRENT); } + /** + * Long screenshot methods + */ + public void takeScreenshot(final int screenshotType, final boolean hasStatus, + final boolean hasNav, @NonNull Handler handler, + final boolean isLongshot, final Bundle screenshotBundle) { + if (screenshotType != WindowManager.TAKE_SCREENSHOT_FULLSCREEN){ + takeScreenshot(screenshotType, hasStatus, hasNav, SCREENSHOT_TIMEOUT_MS, handler, + null); + return; + } + synchronized (mScreenshotLock) { + if (mScreenshotConnection != null) { + return; + } + mHandler = handler; + final Intent serviceIntent = new Intent(); + serviceIntent.setComponent(LongScreenshotManagerService.TAKE_SCREENSHOT_COMPONENT); + ServiceConnection conn = new ServiceConnection() { + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + synchronized (mScreenshotLock) { + if (mScreenshotConnection != this) { + return; + } + Messenger messenger = new Messenger(service); + Message msg = Message.obtain(null, screenshotType); + final ServiceConnection myConn = this; + Handler h = new Handler(handler.getLooper()) { + @Override + public void handleMessage(Message msg) { + synchronized (mScreenshotLock) { + if (msg.what == 2) { + handler.removeCallbacks(mLongshotTimeout); + } else if (mScreenshotConnection == myConn) { + mContext.unbindService(mScreenshotConnection); + mScreenshotConnection = null; + handler.removeCallbacks(mLongshotTimeout); + } + } + } + }; + msg.replyTo = new Messenger(h); + msg.arg1 = hasStatus ? 1: 0; + msg.arg2 = hasNav ? 1: 0; + msg.obj = screenshotBundle; + try { + messenger.send(msg); + } catch (RemoteException e) { + Log.e(TAG, "Couldn't take screenshot: " + e); + } + } + } + + @Override + public void onServiceDisconnected(ComponentName name) { + synchronized (mScreenshotLock) { + if (mScreenshotConnection != null) { + mContext.unbindService(mScreenshotConnection); + mScreenshotConnection = null; + handler.removeCallbacks(mLongshotTimeout); + notifyScreenshotError(); + } + } + } + }; + if (mContext.bindServiceAsUser(serviceIntent, conn, + Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE, + UserHandle.CURRENT)) { + mScreenshotConnection = conn; + if (isLongshot) { + handler.postDelayed(mLongshotTimeout, 120000); + } else { + handler.postDelayed(mLongshotTimeout, SCREENSHOT_TIMEOUT_MS); + } + } + } + } + + public void stopLongshotConnection() { + synchronized (mScreenshotLock) { + if (mScreenshotConnection != null) { + mContext.unbindService(mScreenshotConnection); + mScreenshotConnection = null; + mHandler.removeCallbacks(mLongshotTimeout); + } + } + } + + public void notifyScreenshotCaptureError() { + // If the service process is killed, then ask it to clean up after itself + final ComponentName errorComponent = new ComponentName(SYSUI_PACKAGE, + SYSUI_SCREENSHOT_CAPTURE_ERROR_RECEIVER); + // Broadcast needs to have a valid action. We'll just pick + // a generic one, since the receiver here doesn't care. + Intent errorIntent = new Intent(Intent.ACTION_USER_PRESENT); + errorIntent.setComponent(errorComponent); + errorIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT | + Intent.FLAG_RECEIVER_FOREGROUND); + mContext.sendBroadcastAsUser(errorIntent, UserHandle.CURRENT); + } + } diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl index c29e823218e..4414c0f07e9 100644 --- a/core/java/com/android/internal/view/IInputMethodManager.aidl +++ b/core/java/com/android/internal/view/IInputMethodManager.aidl @@ -71,4 +71,7 @@ interface IInputMethodManager { void reportActivityView(in IInputMethodClient parentClient, int childDisplayId, in float[] matrixValues); + + // Long screenshot + boolean hideSoftInputForLongshot(int flags, in ResultReceiver resultReceiver); } diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java index 9c447983b67..00aedfbcaa1 100644 --- a/packages/SettingsLib/src/com/android/settingslib/Utils.java +++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java @@ -28,6 +28,7 @@ import android.telephony.ServiceState; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.custom.longshot.LongScreenshotManagerService; import com.android.internal.util.UserIcons; import com.android.settingslib.drawable.UserIconDrawable; @@ -318,6 +319,7 @@ public static boolean isSystemPackage(Resources resources, PackageManager pm, Pa || pkg.packageName.equals(sServicesSystemSharedLibPackageName) || pkg.packageName.equals(sSharedSystemSharedLibPackageName) || pkg.packageName.equals(PrintManager.PRINT_SPOOLER_PACKAGE_NAME) + || pkg.packageName.equals(LongScreenshotManagerService.PACKAGENAME_LONGSHOT) || isDeviceProvisioningPackage(resources, pkg.packageName); } diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index c2bc7297279..1dedf0e96fe 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -308,6 +308,14 @@ + + + + + + diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java index b3d9ee03302..34d1611de49 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java @@ -711,7 +711,11 @@ public void onPress() { mHandler.postDelayed(new Runnable() { @Override public void run() { - mScreenshotHelper.takeScreenshot(1, true, true, mHandler, null); + try { + WindowManagerGlobal.getWindowManagerService().takeOPScreenshot(1); + } catch (RemoteException e) { + Log.e(TAG, "Error while trying to take screenshot.", e); + } MetricsLogger.action(mContext, MetricsEvent.ACTION_SCREENSHOT_POWER_MENU); } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java index 8b75d87ceb6..b1db6ba9874 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java @@ -875,7 +875,7 @@ public void onAnimationEnd(Animator animation) { @Override public void run() { if (Settings.System.getInt(mContext.getContentResolver(), - Settings.System.SCREENSHOT_SHUTTER_SOUND, 1) == 1) { + Settings.System.SCREENSHOT_SOUND, 1) == 1) { // Play the shutter sound to notify that we've taken a screenshot mCameraSound.play(MediaActionSound.SHUTTER_CLICK); } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotServiceCaptureErrorReceiver.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotServiceCaptureErrorReceiver.java new file mode 100644 index 00000000000..b7feb73c988 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotServiceCaptureErrorReceiver.java @@ -0,0 +1,40 @@ +/* + * 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.screenshot; + +import android.app.NotificationManager; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; + +import com.android.systemui.R; + +/** + * Performs a number of miscellaneous, non-system-critical actions + * after the system has finished booting. + */ +public class ScreenshotServiceCaptureErrorReceiver extends BroadcastReceiver { + + @Override + public void onReceive(final Context context, Intent intent) { + // Show a message that we've failed to save the image to disk + NotificationManager nm = (NotificationManager) + context.getSystemService(Context.NOTIFICATION_SERVICE); + GlobalScreenshot.notifyScreenshotError(context, nm, + R.string.screenshot_failed_to_capture_text); + } +} diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 63302705b36..5a812f0295a 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -219,6 +219,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub private static final int NOT_A_SUBTYPE_ID = InputMethodUtils.NOT_A_SUBTYPE_ID; private static final String TAG_TRY_SUPPRESSING_IME_SWITCHER = "TrySuppressingImeSwitcher"; + // Long screenshot + private static final long LONGSHOT_BLOCK_SHOWING_TIMEOUT = 1000; + /** * Binding flags for establishing connection to the {@link InputMethodService}. */ @@ -294,6 +297,9 @@ private static final class DebugFlags { new DebugFlag("persist.pre_render_ime_views", false); } + // Long screenshot + private boolean mLongshotBlockShowing = false; + @UserIdInt private int mLastSwitchUserId; @@ -2795,6 +2801,11 @@ boolean showCurrentInputLocked(int flags, ResultReceiver resultReceiver) { return false; } + if (mLongshotBlockShowing) { + Slog.d(TAG, "Longshot Blocking"); + return false; + } + boolean res = false; if (mCurMethod != null) { if (DEBUG) Slog.d(TAG, "showCurrentInputLocked: mCurToken=" + mCurToken); @@ -2865,6 +2876,27 @@ public boolean hideSoftInput(IInputMethodClient client, int flags, } } + @Override + public boolean hideSoftInputForLongshot(int flags, ResultReceiver resultReceiver) { + synchronized (mMethodMap) { + if (!calledFromValidUserLocked()) { + return false; + } + final long ident = Binder.clearCallingIdentity(); + try { + mLongshotBlockShowing = true; + mHandler.postDelayed(new Runnable() { + public void run() { + mLongshotBlockShowing = false; + } + }, LONGSHOT_BLOCK_SHOWING_TIMEOUT); + return hideCurrentInputLocked(flags, resultReceiver); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + } + boolean hideCurrentInputLocked(int flags, ResultReceiver resultReceiver) { if ((flags&InputMethodManager.HIDE_IMPLICIT_ONLY) != 0 && (mShowExplicitlyRequested || mShowForced)) { diff --git a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java index 3dd730471dc..080a50d3501 100644 --- a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java @@ -1512,6 +1512,11 @@ public boolean hideSoftInput( } } + @Override + public boolean hideSoftInputForLongshot(int flags, ResultReceiver resultReceiver) { + return false; + } + @BinderThread @Override public InputBindResult startInputOrWindowGainedFocus( diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 1fda5e6dbde..4561218cc2b 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -201,6 +201,7 @@ import com.android.internal.R; import com.android.internal.accessibility.AccessibilityShortcutController; +import com.android.internal.custom.longshot.ILongScreenshotManager; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto; import com.android.internal.os.DeviceKeyHandler; @@ -1469,7 +1470,8 @@ public void setScreenshotType(int screenshotType) { @Override public void run() { - mDefaultDisplayPolicy.takeScreenshot(mScreenshotType); + boolean dockMinimized = mWindowManagerInternal.isMinimizedDock(); + mDefaultDisplayPolicy.takeScreenshot(mScreenshotType, dockMinimized); } } @@ -1482,6 +1484,7 @@ public void showGlobalActions() { } void showGlobalActionsInternal(boolean hapticFeedback) { + stopLongshot(); final boolean keyguardShowing = isKeyguardShowingAndNotOccluded(); if (keyguardShowing && !isGlobalActionEnabled()) { return; @@ -1499,6 +1502,30 @@ void showGlobalActionsInternal(boolean hapticFeedback) { mPowerManager.userActivity(SystemClock.uptimeMillis(), false); } + private void stopLongshot() { + ILongScreenshotManager shot = ILongScreenshotManager.Stub.asInterface(ServiceManager.getService(Context.LONGSCREENSHOT_SERVICE)); + if (shot != null) { + try { + if (shot.isLongshotMode()) { + shot.stopLongshot(); + } + } catch (RemoteException e) { + Slog.d(TAG, e.toString()); + } + } + } + + @Override + public void stopLongshotConnection() { + mDefaultDisplayPolicy.stopLongshotConnection(); + } + + @Override + public void takeOPScreenshot(int type) { + mScreenshotRunnable.setScreenshotType(type); + mHandler.post(mScreenshotRunnable); + } + boolean isDeviceProvisioned() { return Settings.Global.getInt( mContext.getContentResolver(), Settings.Global.DEVICE_PROVISIONED, 0) != 0; diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java index c77ff7ab600..bd2b3948b3a 100644 --- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java +++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java @@ -1513,4 +1513,11 @@ default void onDefaultDisplayFocusChangedLw(WindowState newFocus) {} * @return whether the value was changed. */ boolean setAodShowing(boolean aodShowing); + + /** + * Long screenshot + * @hide + */ + public void takeOPScreenshot(int type); + public void stopLongshotConnection(); } diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index 341645c2326..22d12e6432f 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -130,14 +130,16 @@ import android.graphics.Region; import android.hardware.input.InputManager; import android.hardware.power.V1_0.PowerHint; +import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.SystemClock; import android.os.SystemProperties; import android.os.UserHandle; -import android.util.BoostFramework; +import android.provider.Settings; import android.util.ArraySet; +import android.util.BoostFramework; import android.util.Pair; import android.util.PrintWriterPrinter; import android.util.Slog; @@ -400,6 +402,8 @@ public void onBarVisibilityChanged(boolean visible) { private RefreshRatePolicy mRefreshRatePolicy; + private int mDisplayRotation; + // -------- PolicyHandler -------- private static final int MSG_UPDATE_DREAMING_SLEEP_TOKEN = 1; private static final int MSG_REQUEST_TRANSIENT_BARS = 2; @@ -1582,6 +1586,7 @@ public void onInputEvent(InputEvent event) { */ public void beginLayoutLw(DisplayFrames displayFrames, int uiMode) { displayFrames.onBeginLayout(); + mDisplayRotation = displayFrames.mRotation; mSystemGestures.screenWidth = displayFrames.mUnrestricted.width(); mSystemGestures.screenHeight = displayFrames.mUnrestricted.height(); @@ -3772,16 +3777,51 @@ public void onLockTaskStateChangedLw(int lockTaskState) { * @param screenshotType The type of screenshot, for example either * {@link WindowManager#TAKE_SCREENSHOT_FULLSCREEN} or * {@link WindowManager#TAKE_SCREENSHOT_SELECTED_REGION} + * @param dockMinimized Whether the Dock is minimized */ - public void takeScreenshot(int screenshotType) { + public void takeScreenshot(int screenshotType, boolean dockMinimized) { if (mScreenshotHelper != null) { + boolean longshot; + boolean inMultiWindow = mFocusedWindow != null + ? mFocusedWindow.inMultiWindowMode() + : false; + if (screenshotType == WindowManager.TAKE_SCREENSHOT_SELECTED_REGION + || mService.mPolicy.isKeyguardOccluded() + || !mService.mPolicy.isUserSetupComplete() + || !isDeviceProvisioned() + || ((inMultiWindow && !dockMinimized) || mDisplayRotation != 0)) { + longshot = false; + } else { + longshot = true; + } + Bundle screenshotBundle = new Bundle(); + screenshotBundle.putBoolean("longshot", longshot); + if (mFocusedWindow != null) { + screenshotBundle.putString("focusWindow", mFocusedWindow.getAttrs().packageName); + } + if (mFocusedWindow != null + && (mFocusedWindow.getAttrs().flags & WindowManager.LayoutParams.FLAG_SECURE) != 0) { + mScreenshotHelper.notifyScreenshotCaptureError(); + return; + } mScreenshotHelper.takeScreenshot(screenshotType, mStatusBar != null && mStatusBar.isVisibleLw(), mNavigationBar != null && mNavigationBar.isVisibleLw(), - mHandler, null /* completionConsumer */); + mHandler, longshot, screenshotBundle); } } + public void stopLongshotConnection() { + if (mScreenshotHelper != null) { + mScreenshotHelper.stopLongshotConnection(); + } + } + + private boolean isDeviceProvisioned() { + return Settings.Global.getInt( + mContext.getContentResolver(), Settings.Global.DEVICE_PROVISIONED, 0) != 0; + } + RefreshRatePolicy getRefreshRatePolicy() { return mRefreshRatePolicy; } diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java index b407ac5e60c..ce2a10e4eb7 100644 --- a/services/core/java/com/android/server/wm/WindowManagerInternal.java +++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java @@ -505,4 +505,8 @@ public abstract void setOnHardKeyboardStatusChangeListener( */ public abstract void removeNonHighRefreshRatePackage(@NonNull String packageName); + /** + * Long screenshot + */ + public abstract boolean isMinimizedDock(); } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index f4f6d25577b..062dcb428d8 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -7591,6 +7591,20 @@ public void removeNonHighRefreshRatePackage(@NonNull String packageName) { .removeNonHighRefreshRatePackage(packageName)); } } + + @Override + public boolean isMinimizedDock() { + boolean isMinimizedDock; + synchronized (mWindowMap) { + try { + boostPriorityForLockedSection(); + isMinimizedDock = getDefaultDisplayContentLocked().getDockedDividerController().isMinimizedDock(); + } finally { + resetPriorityAfterLockedSection(); + } + } + return isMinimizedDock; + } } void registerAppFreezeListener(AppFreezeListener listener) { @@ -7867,4 +7881,14 @@ private void handleDisplayFocusChange(WindowState window) { 0 /* configChanges */, !PRESERVE_WINDOWS, true /* notifyClients */); } } + + @Override + public void stopLongshotConnection() { + mPolicy.stopLongshotConnection(); + } + + @Override + public void takeOPScreenshot(int type) { + mPolicy.takeOPScreenshot(type); + } } diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index a61d2b1dc35..0d219c11af8 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -72,6 +72,7 @@ import android.view.inputmethod.InputMethodSystemProperty; import com.android.internal.R; +import com.android.internal.custom.longshot.LongScreenshotManagerService; import com.android.internal.logging.MetricsLogger; import com.android.internal.notification.SystemNotificationChannels; import com.android.internal.os.BinderInternal; @@ -1101,6 +1102,10 @@ private void startOtherServices() { traceBeginAndSlog("SignedConfigService"); SignedConfigService.registerUpdateReceiver(mSystemContext); traceEnd(); + + traceBeginAndSlog("LongScreenshotManagerService"); + ServiceManager.addService(Context.LONGSCREENSHOT_SERVICE, LongScreenshotManagerService.getInstance(context)); + traceEnd(); } catch (RuntimeException e) { Slog.e("System", "******************************************"); Slog.e("System", "************ Failure starting core service", e); From 641227dbcf72818a5c73156e791fd9ddf31e76dd Mon Sep 17 00:00:00 2001 From: jhenrique09 Date: Wed, 25 Mar 2020 22:08:42 -0300 Subject: [PATCH 03/26] base: Port face unlock feature [3/4] * From motorola/foles_retail/foles:10/QPF30.130-15-7/38ece7:user/release-keys Change-Id: Ic324086a5da214106f7e2517d766d4c6c7192aaa --- .../android/hardware/face/FaceManager.java | 53 ++ .../android/hardware/face/IFaceService.aidl | 5 + services/core/Android.bp | 1 + .../server/biometrics/face/FaceService.java | 457 +++++++++++++++++- 4 files changed, 514 insertions(+), 2 deletions(-) diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java index 12b285a0f0a..af474910f7e 100644 --- a/core/java/android/hardware/face/FaceManager.java +++ b/core/java/android/hardware/face/FaceManager.java @@ -244,6 +244,59 @@ public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSi } } + /** + * Request face authentication enrollment. This call operates the face authentication hardware + * and starts capturing images. Progress will be indicated by callbacks to the + * {@link EnrollmentCallback} object. It terminates when + * {@link EnrollmentCallback#onEnrollmentError(int, CharSequence)} or + * {@link EnrollmentCallback#onEnrollmentProgress(int) is called with remaining == 0, at + * which point the object is no longer valid. The operation can be canceled by using the + * provided cancel object. + * + * @param token a unique token provided by a recent creation or verification of device + * credentials (e.g. pin, pattern or password). + * @param cancel an object that can be used to cancel enrollment + * @param flags optional flags + * @param callback an object to receive enrollment events + * @hide + */ + @RequiresPermission(MANAGE_BIOMETRIC) + public void enroll(byte[] token, CancellationSignal cancel, + EnrollmentCallback callback, int[] disabledFeatures) { + if (callback == null) { + throw new IllegalArgumentException("Must supply an enrollment callback"); + } + + if (cancel != null) { + if (cancel.isCanceled()) { + Log.w(TAG, "enrollment already canceled"); + return; + } else { + cancel.setOnCancelListener(new OnEnrollCancelListener()); + } + } + + if (mService != null) { + try { + mEnrollmentCallback = callback; + Trace.beginSection("FaceManager#enroll"); + mService.enrollMoto(mToken, token, mServiceReceiver, + mContext.getOpPackageName(), disabledFeatures); + } catch (RemoteException e) { + Log.w(TAG, "Remote exception in enroll: ", e); + if (callback != null) { + // Though this may not be a hardware issue, it will cause apps to give up or + // try again later. + callback.onEnrollmentError(FACE_ERROR_HW_UNAVAILABLE, + getErrorString(mContext, FACE_ERROR_HW_UNAVAILABLE, + 0 /* vendorCode */)); + } + } finally { + Trace.endSection(); + } + } + } + /** * Request face authentication enrollment. This call operates the face authentication hardware * and starts capturing images. Progress will be indicated by callbacks to the diff --git a/core/java/android/hardware/face/IFaceService.aidl b/core/java/android/hardware/face/IFaceService.aidl index b6a0afbf716..9aa34d4738d 100644 --- a/core/java/android/hardware/face/IFaceService.aidl +++ b/core/java/android/hardware/face/IFaceService.aidl @@ -105,4 +105,9 @@ interface IFaceService { void getFeature(int userId, int feature, IFaceServiceReceiver receiver, String opPackageName); void userActivity(); + + // Moto additions + // Start face enrollment + void enrollMoto(IBinder token, in byte [] cryptoToken, IFaceServiceReceiver receiver, + String opPackageName, in int [] disabledFeatures); } diff --git a/services/core/Android.bp b/services/core/Android.bp index 9855e4ea339..5f0199ec45d 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -54,6 +54,7 @@ java_library_static { "dnsresolver_aidl_interface-V2-java", "netd_aidl_interface-V2-java", "netd_event_listener_interface-java", + "faceunlock_utils", ], } diff --git a/services/core/java/com/android/server/biometrics/face/FaceService.java b/services/core/java/com/android/server/biometrics/face/FaceService.java index 387d7a85f4a..4d221fa2daf 100644 --- a/services/core/java/com/android/server/biometrics/face/FaceService.java +++ b/services/core/java/com/android/server/biometrics/face/FaceService.java @@ -27,8 +27,14 @@ import android.app.NotificationChannel; import android.app.NotificationManager; import android.app.PendingIntent; +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.BiometricAuthenticator; import android.hardware.biometrics.BiometricConstants; @@ -46,6 +52,7 @@ import android.os.Binder; import android.os.Build; import android.os.Environment; +import android.os.Handler; import android.os.IBinder; import android.os.NativeHandle; import android.os.RemoteException; @@ -55,10 +62,12 @@ import android.os.UserManager; import android.provider.Settings; import android.util.Slog; +import android.util.SparseArray; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.logging.MetricsLogger; +import com.android.internal.os.BackgroundThread; import com.android.internal.util.DumpUtils; import com.android.server.SystemServerInitThreadPool; import com.android.server.biometrics.AuthenticationClient; @@ -84,6 +93,10 @@ import java.util.List; import java.util.Map; +import com.android.internal.util.custom.faceunlock.FaceUnlockUtils; +import com.motorola.internal.app.IMotoFaceService; +import com.motorola.internal.app.IMotoFaceServiceReceiver; + /** * A service to manage multiple clients that want to access the face HAL API. * The service is responsible for maintaining a list of clients and dispatching all @@ -103,6 +116,230 @@ public class FaceService extends BiometricServiceBase { private static final String NOTIFICATION_TAG = "FaceService"; private static final int NOTIFICATION_ID = 1; + + /** Start moto changes */ + + private static final int MOTO_DEVICE_ID = 1108; + private static final String BIND_MOTOFACEID_ACTION = "com.motorola.faceunlock.BIND"; + private static final String PACKAGE_MOTOFACEID_PACKAGE_NAME = "com.motorola.faceunlock"; + private static final String PACKAGE_MOTOFACEID_SERVICE_NAME = "com.motorola.faceunlock.service.FaceAuthService"; + + SparseArray mMotoFaceServices = new SparseArray<>(); + IMotoFaceServiceReceiver mMotoReceiver = new IMotoFaceServiceReceiver.Stub() { + @Override + public void onEnrollResult(int faceId, int userId, int remaining) { + mHandler.post(new Runnable() { + @Override + public final void run() { + FaceService.super.handleEnrollResult(new Face(getBiometricUtils().getUniqueName(getContext(), userId), faceId, MOTO_DEVICE_ID), remaining); + } + }); + } + + @Override + public void onAuthenticated(int faceId, int userId, byte[] token) { + mHandler.post(new Runnable() { + @Override + public final void run() { + Face face = new Face("", faceId, MOTO_DEVICE_ID); + ArrayList token_AL = new ArrayList<>(token.length); + for (byte b : token) { + token_AL.add(new Byte(b)); + } + FaceService.super.handleAuthenticated(face, token_AL); + } + }); + } + + @Override + public void onAcquired(int userId, int acquiredInfo, int vendorCode) { + mHandler.post(new Runnable() { + @Override + public final void run() { + FaceService.super.handleAcquired(MOTO_DEVICE_ID, acquiredInfo, vendorCode); + } + }); + } + + @Override + public void onError(int error, int vendorCode) { + mHandler.post(new Runnable() { + @Override + public final void run() { + FaceService.super.handleError(MOTO_DEVICE_ID, error, vendorCode); + } + }); + } + + @Override + public void onRemoved(int[] faceIds, int userId) throws RemoteException { + mHandler.post(new Runnable() { + @Override + public final void run() { + if (faceIds.length > 0) { + for (int i = 0; i < faceIds.length; i++) { + FaceService.super.handleRemoved(new Face("", faceIds[i], MOTO_DEVICE_ID), (faceIds.length - i) - 1); + } + return; + } + FaceService.super.handleRemoved(new Face("", 0, MOTO_DEVICE_ID), 0); + } + }); + } + + @Override + public void onEnumerate(int[] faceIds, int userId) throws RemoteException { + mHandler.post(new Runnable() { + @Override + public final void run() { + if (faceIds.length > 0) { + for (int i = 0; i < faceIds.length; i++) { + FaceService.super.handleEnumerate(new Face("", faceIds[i], MOTO_DEVICE_ID), (faceIds.length - i) - 1); + } + return; + } + FaceService.super.handleEnumerate(null, 0); + } + }); + } + + @Override + public void onLockoutChanged(long duration) throws RemoteException { + if (duration == 0) { + mCurrentUserLockoutMode = 0; + } else if (duration == Long.MAX_VALUE) { + mCurrentUserLockoutMode = 2; + } else { + mCurrentUserLockoutMode = 1; + } + mHandler.post(new Runnable() { + @Override + public final void run() { + if (duration == 0) { + notifyLockoutResetMonitors(); + } + } + }); + } + }; + private Handler mMotoServiceHandler; + public boolean mIsMotoServiceBinding = false; + private static final boolean mUseMotoFaceUnlockService = FaceUnlockUtils.hasMotoFaceUnlock(); + private final BroadcastReceiver mUserUnlockReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (mUseMotoFaceUnlockService) { + if (getMotoFaceService(mCurrentUserId) == null) { + bindMotoFaceAuthService(mCurrentUserId); + } + } + } + }; + + private class MotoFaceServiceConnection implements ServiceConnection { + int mUserId; + + public MotoFaceServiceConnection(int userId) { + mUserId = userId; + } + + @Override + public void onServiceConnected(ComponentName className, IBinder service) { + Slog.d(TAG, "MotoFaceService connected"); + IMotoFaceService motoFaceService = IMotoFaceService.Stub.asInterface(service); + if (motoFaceService != null) { + synchronized (mMotoFaceServices) { + try { + motoFaceService.setCallback(mMotoReceiver); + motoFaceService.asBinder().linkToDeath(new IBinder.DeathRecipient() { + @Override + public void binderDied() { + Slog.e(TAG, "MotoFaceService binder died"); + mMotoFaceServices.remove(mUserId); + if (mUserId == mCurrentUserId) { + boolean unused = bindMotoFaceAuthService(mUserId); + } + } + }, 0); + mMotoFaceServices.put(mUserId, motoFaceService); + mHandler.post(new Runnable() { + @Override + public final void run() { + if (mMotoFaceServices.size() == 1) { + loadAuthenticatorIds(); + } + updateActiveGroup(mUserId, null); + doTemplateCleanupForUser(mUserId); + } + }); + } catch (RemoteException e) { + e.printStackTrace(); + } + mIsMotoServiceBinding = false; + } + } + } + + @Override + public void onServiceDisconnected(ComponentName className) { + Slog.d(TAG, "MotoFaceService disconnected"); + mMotoFaceServices.remove(mUserId); + mIsMotoServiceBinding = false; + if (mUserId == mCurrentUserId) { + bindMotoFaceAuthService(mUserId); + } + } + } + + private boolean isMotoFaceServiceEnabled() { + PackageManager pm = getContext().getPackageManager(); + if (!mUseMotoFaceUnlockService) { + return false; + } + Intent intent = new Intent(BIND_MOTOFACEID_ACTION); + intent.setClassName(PACKAGE_MOTOFACEID_PACKAGE_NAME, PACKAGE_MOTOFACEID_SERVICE_NAME); + ResolveInfo info = pm.resolveService(intent, 131072); + if (info == null || !info.serviceInfo.isEnabled()) { + return false; + } + return true; + } + + private IMotoFaceService getMotoFaceService(int userId) { + if (userId == UserHandle.USER_NULL) { + updateActiveGroup(ActivityManager.getCurrentUser(), null); + } + return mMotoFaceServices.get(mCurrentUserId); + } + + private boolean bindMotoFaceAuthService(int userId) { + Slog.d(TAG, "bindMotoFaceAuthService"); + if (!isMotoFaceServiceEnabled()) { + Slog.d(TAG, "MotoFaceService disabled"); + return false; + } else if (mIsMotoServiceBinding) { + Slog.d(TAG, "MotoFaceService is binding"); + return true; + } else { + if (userId != UserHandle.USER_NULL && getMotoFaceService(userId) == null) { + try { + Intent intent = new Intent(BIND_MOTOFACEID_ACTION); + intent.setClassName(PACKAGE_MOTOFACEID_PACKAGE_NAME, PACKAGE_MOTOFACEID_SERVICE_NAME); + boolean result = getContext().bindServiceAsUser(intent, new MotoFaceServiceConnection(userId), 65, UserHandle.of(userId)); + if (result) { + mIsMotoServiceBinding = true; + } + return result; + } catch (Exception e) { + Slog.e(TAG, "bindMotoFaceAuthService failed", e); + } + } + return false; + } + } + + /* End moto changes*/ + /** * Events for bugreports. */ @@ -219,11 +456,17 @@ protected int statsModality() { @Override public boolean shouldFrameworkHandleLockout() { + if (mUseMotoFaceUnlockService){ + return true; + } return false; } @Override public boolean wasUserDetected() { + if (mUseMotoFaceUnlockService){ + return mLastAcquire != FaceManager.FACE_ACQUIRED_NOT_DETECTED; + } return mLastAcquire != FaceManager.FACE_ACQUIRED_NOT_DETECTED && mLastAcquire != FaceManager.FACE_ACQUIRED_SENSOR_DIRTY; } @@ -361,6 +604,42 @@ public int revokeChallenge(IBinder token) { return Status.OK; } + @Override // Binder call + public void enrollMoto(final IBinder token, final byte[] cryptoToken, + final IFaceServiceReceiver receiver, final String opPackageName, + final int[] disabledFeatures) { + checkPermission(MANAGE_BIOMETRIC); + + final boolean restricted = isRestricted(); + final EnrollClientImpl client = new EnrollClientImpl(getContext(), mDaemonWrapper, + mHalDeviceId, token, new ServiceListenerImpl(receiver), mCurrentUserId, + 0 /* groupId */, cryptoToken, restricted, opPackageName, disabledFeatures, + ENROLL_TIMEOUT_SEC) { + + @Override + public int[] getAcquireIgnorelist() { + return mEnrollIgnoreList; + } + + @Override + public int[] getAcquireVendorIgnorelist() { + return mEnrollIgnoreListVendor; + } + + @Override + public boolean shouldVibrate() { + return false; + } + + @Override + protected int statsModality() { + return FaceService.this.statsModality(); + } + }; + + enrollInternal(client, mCurrentUserId); + } + @Override // Binder call public void enroll(int userId, final IBinder token, final byte[] cryptoToken, final IFaceServiceReceiver receiver, final String opPackageName, @@ -542,6 +821,20 @@ public boolean isHardwareDetected(long deviceId, String opPackageName) { UserHandle.getCallingUserId())) { return false; } + if (mUseMotoFaceUnlockService) { + boolean enabled = isMotoFaceServiceEnabled(); + if (enabled) { + mHandler.post(new Runnable() { + @Override + public final void run() { + if (getMotoFaceService(mCurrentUserId) == null) { + bindMotoFaceAuthService(mCurrentUserId); + } + } + }); + } + return enabled; + } final long token = Binder.clearCallingIdentity(); try { @@ -977,6 +1270,20 @@ public void onLockoutChanged(long duration) { private final DaemonWrapper mDaemonWrapper = new DaemonWrapper() { @Override public int authenticate(long operationId, int groupId) throws RemoteException { + if (mUseMotoFaceUnlockService) { + IMotoFaceService service = getMotoFaceService(mCurrentUserId); + if (service != null) { + try{ + service.authenticate(operationId); + } catch (Exception e) { + Slog.e(TAG, "authenticate failed", e); + } + return 0; + } + bindMotoFaceAuthService(mCurrentUserId); + Slog.w(TAG, "authenticate(): moto face service not started!"); + return 3; + } IBiometricsFace daemon = getFaceDaemon(); if (daemon == null) { Slog.w(TAG, "authenticate(): no face HAL!"); @@ -987,6 +1294,19 @@ public int authenticate(long operationId, int groupId) throws RemoteException { @Override public int cancel() throws RemoteException { + if (mUseMotoFaceUnlockService) { + IMotoFaceService service = getMotoFaceService(mCurrentUserId); + if (service == null) { + return 0; + } + try{ + service.cancel(); + }catch (Exception e) { + Slog.e(TAG, "cancel failed", e); + } + service.cancel(); + return 0; + } IBiometricsFace daemon = getFaceDaemon(); if (daemon == null) { Slog.w(TAG, "cancel(): no face HAL!"); @@ -997,6 +1317,20 @@ public int cancel() throws RemoteException { @Override public int remove(int groupId, int biometricId) throws RemoteException { + if (mUseMotoFaceUnlockService) { + IMotoFaceService service = getMotoFaceService(mCurrentUserId); + if (service != null) { + try{ + service.remove(biometricId); + }catch (Exception e) { + Slog.e(TAG, "remove failed", e); + } + return 0; + } + bindMotoFaceAuthService(mCurrentUserId); + Slog.w(TAG, "remove(): moto face service not started!"); + return 3; + } IBiometricsFace daemon = getFaceDaemon(); if (daemon == null) { Slog.w(TAG, "remove(): no face HAL!"); @@ -1007,6 +1341,26 @@ public int remove(int groupId, int biometricId) throws RemoteException { @Override public int enumerate() throws RemoteException { + if (mUseMotoFaceUnlockService) { + IMotoFaceService service = getMotoFaceService(mCurrentUserId); + if (service != null) { + mMotoServiceHandler.post(new Runnable() { + @Override + public final void run() { + try { + service.enumerate(); + } catch (Exception e) { + Slog.e(TAG, "enumerate failed", e); + FaceService.super.handleError(MOTO_DEVICE_ID, 8, 0); + } + } + }); + return 0; + } + bindMotoFaceAuthService(mCurrentUserId); + Slog.w(TAG, "enumerate(): moto face service not started!"); + return 3; + } IBiometricsFace daemon = getFaceDaemon(); if (daemon == null) { Slog.w(TAG, "enumerate(): no face HAL!"); @@ -1018,6 +1372,27 @@ public int enumerate() throws RemoteException { @Override public int enroll(byte[] cryptoToken, int groupId, int timeout, ArrayList disabledFeatures) throws RemoteException { + if (mUseMotoFaceUnlockService) { + IMotoFaceService service = getMotoFaceService(mCurrentUserId); + int[] dfs = null; + if (disabledFeatures != null && disabledFeatures.size() > 0) { + dfs = new int[disabledFeatures.size()]; + for (int i = 0; i < disabledFeatures.size(); i++) { + dfs[i] = disabledFeatures.get(i).intValue(); + } + } + if (service != null) { + try{ + service.enroll(cryptoToken, timeout, dfs); + }catch (Exception e) { + Slog.e(TAG, "enroll failed", e); + } + return 0; + } + bindMotoFaceAuthService(mCurrentUserId); + Slog.w(FaceService.TAG, "enroll(): moto face service not started!"); + return 3; + } IBiometricsFace daemon = getFaceDaemon(); if (daemon == null) { Slog.w(TAG, "enroll(): no face HAL!"); @@ -1032,6 +1407,20 @@ public int enroll(byte[] cryptoToken, int groupId, int timeout, @Override public void resetLockout(byte[] cryptoToken) throws RemoteException { + if (mUseMotoFaceUnlockService) { + IMotoFaceService service = getMotoFaceService(mCurrentUserId); + if (service != null) { + try{ + service.resetLockout(cryptoToken); + }catch (Exception e) { + Slog.e(TAG, "resetLockout failed", e); + } + return; + } + bindMotoFaceAuthService(mCurrentUserId); + Slog.w(TAG, "resetLockout(): moto face service not started!"); + return; + } IBiometricsFace daemon = getFaceDaemon(); if (daemon == null) { Slog.w(TAG, "resetLockout(): no face HAL!"); @@ -1065,6 +1454,8 @@ public FaceService(Context context) { .getIntArray(R.array.config_face_acquire_enroll_ignorelist); mEnrollIgnoreListVendor = getContext().getResources() .getIntArray(R.array.config_face_acquire_vendor_enroll_ignorelist); + + context.registerReceiver(mUserUnlockReceiver, new IntentFilter(Intent.ACTION_USER_UNLOCKED)); } @Override @@ -1080,6 +1471,11 @@ protected void removeClient(ClientMonitor client) { public void onStart() { super.onStart(); publishBinderService(Context.FACE_SERVICE, new FaceServiceWrapper()); + if (mUseMotoFaceUnlockService) { + mMotoServiceHandler = BackgroundThread.getHandler(); + mHalDeviceId = MOTO_DEVICE_ID; + return; + } // Get the face daemon on FaceService's on thread so SystemServerInitThreadPool isn't // blocked SystemServerInitThreadPool.get().submit(() -> mHandler.post(this::getFaceDaemon), @@ -1128,6 +1524,28 @@ public void serviceDied(long cookie) { @Override protected void updateActiveGroup(int userId, String clientPackage) { + if (mUseMotoFaceUnlockService) { + mCurrentUserId = userId; + IMotoFaceService service = getMotoFaceService(mCurrentUserId); + if (service != null) { + try { + Map map = mAuthenticatorIds; + Integer valueOf = Integer.valueOf(mCurrentUserId); + long authId = 0; + if (hasEnrolledBiometrics(mCurrentUserId)) { + authId = (long) service.getAuthenticatorId(); + } + map.put(valueOf, Long.valueOf(authId)); + } catch (Exception e) { + Slog.e(TAG, "getAuthenticatorId failed", e); + } + } else { + bindMotoFaceAuthService(mCurrentUserId); + Slog.w(TAG, "updateActiveGroup(): moto face service not started!"); + } + return; + } + IBiometricsFace daemon = getFaceDaemon(); if (daemon != null) { @@ -1178,7 +1596,16 @@ protected long getHalDeviceId() { @Override protected void handleUserSwitching(int userId) { - super.handleUserSwitching(userId); + if (mUseMotoFaceUnlockService) { + updateActiveGroup(userId, null); + if (getMotoFaceService(userId) != null) { + doTemplateCleanupForUser(userId); + } else { + bindMotoFaceAuthService(userId); + } + } else { + super.handleUserSwitching(userId); + } // Will be updated when we get the callback from HAL mCurrentUserLockoutMode = AuthenticationClient.LOCKOUT_NONE; } @@ -1267,6 +1694,20 @@ private synchronized IBiometricsFace getFaceDaemon() { } private long startGenerateChallenge(IBinder token) { + if (mUseMotoFaceUnlockService) { + IMotoFaceService service = getMotoFaceService(mCurrentUserId); + if (service != null) { + try { + return service.generateChallenge(CHALLENGE_TIMEOUT_SEC); + } catch (Exception e) { + Slog.e(TAG, "generateChallenge failed", e); + } + } else { + bindMotoFaceAuthService(mCurrentUserId); + Slog.w(TAG, "startGenerateChallenge(): moto face service not started!"); + } + return 0; + } IBiometricsFace daemon = getFaceDaemon(); if (daemon == null) { Slog.w(TAG, "startGenerateChallenge: no face HAL!"); @@ -1281,6 +1722,17 @@ private long startGenerateChallenge(IBinder token) { } private int startRevokeChallenge(IBinder token) { + if (mUseMotoFaceUnlockService) { + IMotoFaceService service = getMotoFaceService(mCurrentUserId); + if (service != null) { + try { + return service.revokeChallenge(); + } catch (Exception e) { + Slog.e(TAG, "startRevokeChallenge failed", e); + } + } + return 0; + } IBiometricsFace daemon = getFaceDaemon(); if (daemon == null) { Slog.w(TAG, "startRevokeChallenge: no face HAL!"); @@ -1355,7 +1807,8 @@ private void dumpHal(FileDescriptor fd, String[] args) { // Additionally, this flag allows turning off face for a device // (either permanently through the build or on an individual device). if (SystemProperties.getBoolean("ro.face.disable_debug_data", false) - || SystemProperties.getBoolean("persist.face.disable_debug_data", false)) { + || SystemProperties.getBoolean("persist.face.disable_debug_data", false) + || mUseMotoFaceUnlockService) { return; } From aed276181055d88b1e2abc999c3d78f3c0c5016f Mon Sep 17 00:00:00 2001 From: jhenrique09 Date: Sat, 28 Mar 2020 12:51:07 -0300 Subject: [PATCH 04/26] SystemUI: Allow listening for face only on pin/pass view Useful for devices with popup camera Change-Id: Ifba770dd1681858844d5d0290cbf385bfa47f841 --- packages/SystemUI/res/values/reloaded_config.xml | 3 +++ .../android/keyguard/KeyguardUpdateMonitor.java | 14 ++++++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/packages/SystemUI/res/values/reloaded_config.xml b/packages/SystemUI/res/values/reloaded_config.xml index bb424179aa0..01ae671a049 100644 --- a/packages/SystemUI/res/values/reloaded_config.xml +++ b/packages/SystemUI/res/values/reloaded_config.xml @@ -20,4 +20,7 @@ 4 5 + + false + diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index 89d13ede603..ece84f6b0e5 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -263,6 +263,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { private boolean mLockIconPressed; private int mActiveMobileDataSubscription = SubscriptionManager.INVALID_SUBSCRIPTION_ID; + private final boolean mFaceAuthOnlyOnSecurityView; + /** * Short delay before restarting biometric authentication after a successful try * This should be slightly longer than the time between onAuthenticated @@ -1541,6 +1543,8 @@ protected KeyguardUpdateMonitor(Context context) { mSubscriptionManager = SubscriptionManager.from(context); mDeviceProvisioned = isDeviceProvisionedInSettingsDb(); mStrongAuthTracker = new StrongAuthTracker(context, this::notifyStrongAuthStateChanged); + mFaceAuthOnlyOnSecurityView = mContext.getResources().getBoolean( + R.bool.config_faceAuthOnlyOnSecurityView); // Since device can't be un-provisioned, we only need to register a content observer // to update mDeviceProvisioned when we are... @@ -1745,7 +1749,7 @@ private boolean shouldListenForFingerprint() { * If face auth is allows to scan on this exact moment. */ public boolean shouldListenForFace() { - final boolean awakeKeyguard = mKeyguardIsVisible && mDeviceInteractive && !mGoingToSleep; + boolean awakeKeyguard = mKeyguardIsVisible && mDeviceInteractive && !mGoingToSleep; final int user = getCurrentUser(); final int strongAuth = mStrongAuthTracker.getStrongAuthForUser(user); final boolean isLockOutOrLockDown = @@ -1768,13 +1772,19 @@ public boolean shouldListenForFace() { boolean strongAuthAllowsScanning = (!isEncryptedOrTimedOut || canBypass && !mBouncer) && !isLockOutOrLockDown; + boolean unlockPossible = true; + if ((!mBouncer || !awakeKeyguard) && mFaceAuthOnlyOnSecurityView){ + unlockPossible = false; + } + // Only listen if this KeyguardUpdateMonitor belongs to the primary user. There is an // instance of KeyguardUpdateMonitor for each user but KeyguardUpdateMonitor is user-aware. return (mBouncer || mAuthInterruptActive || awakeKeyguard || shouldListenForFaceAssistant()) && !mSwitchingUser && !isFaceDisabled(user) && becauseCannotSkipBouncer && !mKeyguardGoingAway && mFaceSettingEnabledForUser.get(user) && !mLockIconPressed && strongAuthAllowsScanning && mIsPrimaryUser - && !mSecureCameraLaunched; + && !mSecureCameraLaunched + && unlockPossible; } /** From 3d02976b2c601a73175102cdfa122f3084ddade9 Mon Sep 17 00:00:00 2001 From: Ethan Halsall Date: Thu, 9 Apr 2020 23:35:54 +0100 Subject: [PATCH 05/26] base: do not use new lockscreen layout for bypass - bypassEnabled will always evaluate to false now. - Introduce bypassEnabledBiometric which is only used to tell the biometric service whether we can bypass lockscreen without swipe dependant on Settings.Secure.FACE_UNLOCK_DISMISSES_KEYGUARD. Signed-off-by: Ethan Halsall Signed-off-by: Chenyang Zhong Change-Id: Ibdbf16df18543382219a75ce1aaf4e25f3fb4ddb --- .../systemui/statusbar/phone/BiometricUnlockController.java | 2 +- .../systemui/statusbar/phone/KeyguardBypassController.kt | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) 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 41c6a7ba784..ac1b90d5d0c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java @@ -440,7 +440,7 @@ public int getMode() { private @WakeAndUnlockMode int calculateModeForPassiveAuth() { boolean unlockingAllowed = mUpdateMonitor.isUnlockingWithBiometricAllowed(); boolean deviceDreaming = mUpdateMonitor.isDreaming(); - boolean bypass = mKeyguardBypassController.getBypassEnabled(); + boolean bypass = mKeyguardBypassController.getBypassEnabledBiometric(); if (!mUpdateMonitor.isDeviceInteractive()) { if (!mStatusBarKeyguardViewManager.isShowing()) { 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 832ea9e3d72..bcab3830c92 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt @@ -47,6 +47,8 @@ class KeyguardBypassController { * If face unlock dismisses the lock screen or keeps user on keyguard for the current user. */ var bypassEnabled: Boolean = false + + var bypassEnabledBiometric: Boolean = false get() = field && unlockMethodCache.isFaceAuthEnabled private set @@ -88,7 +90,7 @@ class KeyguardBypassController { 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 + bypassEnabledBiometric = tunerService.getValue(key, dismissByDefault) != 0 } }, Settings.Secure.FACE_UNLOCK_DISMISSES_KEYGUARD) lockscreenUserManager.addUserChangedListener { pendingUnlockType = null } From 1aff649ee6e91d2ef5f4a47654d9daaf215398c1 Mon Sep 17 00:00:00 2001 From: DennySPB Date: Mon, 26 Aug 2019 16:03:02 +0300 Subject: [PATCH 06/26] base: Grant storage permissions to Google Markup Signed-off-by: Pranav Vashi --- .../server/pm/permission/DefaultPermissionGrantPolicy.java | 4 ++++ 1 file changed, 4 insertions(+) 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 ecf66861a40..a2e3a9800d2 100644 --- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java +++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java @@ -657,6 +657,10 @@ private void grantDefaultSystemHandlerPermissions(int userId) { getDefaultSystemHandlerActivityPackage(musicIntent, userId), userId, STORAGE_PERMISSIONS); + // Google Markup + grantSystemFixedPermissionsToSystemPackage("com.google.android.markup", userId, + STORAGE_PERMISSIONS); + // Home Intent homeIntent = new Intent(Intent.ACTION_MAIN) .addCategory(Intent.CATEGORY_HOME) From ca597445e7dd13327ae1331715aef6fa440caf2a Mon Sep 17 00:00:00 2001 From: jhenrique09 Date: Thu, 12 Sep 2019 23:06:07 -0300 Subject: [PATCH 07/26] SystemUI: Implement Smart space * Includes weather on lockscreen and cool stuff on ambient display * Code reverse engineered from crosshatch * Requires Google app installed @SKULSHADY : Fix SystemUIGoogle after r16 merge Change-Id: Ice5ea4fd43412cf916daefa6521fc8b55f14f199 Co-authored-by: Anushek Prasal Signed-off-by: Mesquita --- packages/SystemUI/AndroidManifest.xml | 13 +- packages/SystemUI/proguard.flags | 3 + .../res/values-af-rZA/smartspace_plurals.xml | 12 + .../res/values-af-rZA/smartspace_strings.xml | 6 + .../res/values-am-rET/smartspace_plurals.xml | 12 + .../res/values-am-rET/smartspace_strings.xml | 6 + .../res/values-ar-rSA/smartspace_plurals.xml | 20 ++ .../res/values-ar-rSA/smartspace_strings.xml | 6 + .../res/values-as-rIN/smartspace_plurals.xml | 12 + .../res/values-as-rIN/smartspace_strings.xml | 6 + .../res/values-az-rAZ/smartspace_plurals.xml | 12 + .../res/values-az-rAZ/smartspace_strings.xml | 6 + .../res/values-be-rBY/smartspace_plurals.xml | 16 + .../res/values-be-rBY/smartspace_strings.xml | 6 + .../res/values-bg-rBG/smartspace_plurals.xml | 12 + .../res/values-bg-rBG/smartspace_strings.xml | 6 + .../res/values-bn-rBD/smartspace_plurals.xml | 12 + .../res/values-bn-rBD/smartspace_strings.xml | 6 + .../res/values-bs-rBA/smartspace_plurals.xml | 14 + .../res/values-bs-rBA/smartspace_strings.xml | 6 + .../res/values-ca-rES/smartspace_plurals.xml | 12 + .../res/values-ca-rES/smartspace_strings.xml | 6 + .../res/values-cs-rCZ/smartspace_plurals.xml | 16 + .../res/values-cs-rCZ/smartspace_strings.xml | 6 + .../res/values-da-rDK/smartspace_plurals.xml | 12 + .../res/values-da-rDK/smartspace_strings.xml | 6 + .../res/values-de-rDE/smartspace_plurals.xml | 12 + .../res/values-de-rDE/smartspace_strings.xml | 6 + .../res/values-el-rGR/smartspace_plurals.xml | 12 + .../res/values-el-rGR/smartspace_strings.xml | 6 + .../res/values-es-rES/smartspace_plurals.xml | 12 + .../res/values-es-rES/smartspace_strings.xml | 6 + .../res/values-es-rMX/smartspace_plurals.xml | 12 + .../res/values-es-rMX/smartspace_strings.xml | 6 + .../res/values-et-rEE/smartspace_plurals.xml | 12 + .../res/values-et-rEE/smartspace_strings.xml | 6 + .../res/values-eu-rES/smartspace_plurals.xml | 12 + .../res/values-eu-rES/smartspace_strings.xml | 6 + .../res/values-fa-rIR/smartspace_plurals.xml | 12 + .../res/values-fa-rIR/smartspace_strings.xml | 6 + .../res/values-fi-rFI/smartspace_plurals.xml | 12 + .../res/values-fi-rFI/smartspace_strings.xml | 6 + .../res/values-fr-rFR/smartspace_plurals.xml | 12 + .../res/values-fr-rFR/smartspace_strings.xml | 6 + .../res/values-gl-rES/smartspace_plurals.xml | 12 + .../res/values-gl-rES/smartspace_strings.xml | 6 + .../res/values-gu-rIN/smartspace_plurals.xml | 12 + .../res/values-gu-rIN/smartspace_strings.xml | 6 + .../res/values-hi-rIN/smartspace_plurals.xml | 12 + .../res/values-hi-rIN/smartspace_strings.xml | 6 + .../res/values-hr-rHR/smartspace_plurals.xml | 14 + .../res/values-hr-rHR/smartspace_strings.xml | 6 + .../res/values-hu-rHU/smartspace_plurals.xml | 12 + .../res/values-hu-rHU/smartspace_strings.xml | 6 + .../res/values-hy-rAM/smartspace_plurals.xml | 12 + .../res/values-hy-rAM/smartspace_strings.xml | 6 + .../res/values-in-rID/smartspace_plurals.xml | 10 + .../res/values-is-rIS/smartspace_plurals.xml | 12 + .../res/values-is-rIS/smartspace_strings.xml | 6 + .../res/values-it-rIT/smartspace_plurals.xml | 12 + .../res/values-it-rIT/smartspace_strings.xml | 6 + .../res/values-iw-rIL/smartspace_strings.xml | 6 + .../res/values-ja-rJP/smartspace_plurals.xml | 10 + .../res/values-ja-rJP/smartspace_strings.xml | 6 + .../res/values-ka-rGE/smartspace_plurals.xml | 12 + .../res/values-ka-rGE/smartspace_strings.xml | 6 + .../res/values-kk-rKZ/smartspace_plurals.xml | 12 + .../res/values-kk-rKZ/smartspace_strings.xml | 6 + .../res/values-km-rKH/smartspace_plurals.xml | 10 + .../res/values-km-rKH/smartspace_strings.xml | 6 + .../res/values-kn-rIN/smartspace_plurals.xml | 12 + .../res/values-kn-rIN/smartspace_strings.xml | 6 + .../res/values-ko-rKR/smartspace_plurals.xml | 10 + .../res/values-ko-rKR/smartspace_strings.xml | 6 + .../res/values-ky-rKG/smartspace_plurals.xml | 12 + .../res/values-ky-rKG/smartspace_strings.xml | 6 + .../res/values-lo-rLA/smartspace_plurals.xml | 10 + .../res/values-lo-rLA/smartspace_strings.xml | 6 + .../res/values-lt-rLT/smartspace_plurals.xml | 16 + .../res/values-lt-rLT/smartspace_strings.xml | 6 + .../res/values-lv-rLV/smartspace_plurals.xml | 14 + .../res/values-lv-rLV/smartspace_strings.xml | 6 + .../res/values-mk-rMK/smartspace_plurals.xml | 12 + .../res/values-mk-rMK/smartspace_strings.xml | 6 + .../res/values-mn-rMN/smartspace_plurals.xml | 12 + .../res/values-mn-rMN/smartspace_strings.xml | 6 + .../res/values-mr-rIN/smartspace_plurals.xml | 12 + .../res/values-mr-rIN/smartspace_strings.xml | 6 + .../res/values-ms-rMY/smartspace_plurals.xml | 10 + .../res/values-ms-rMY/smartspace_strings.xml | 6 + .../res/values-my-rMM/smartspace_plurals.xml | 10 + .../res/values-my-rMM/smartspace_strings.xml | 6 + .../res/values-nb-rNO/smartspace_plurals.xml | 12 + .../res/values-nb-rNO/smartspace_strings.xml | 6 + .../res/values-nl-rNL/smartspace_plurals.xml | 12 + .../res/values-nl-rNL/smartspace_strings.xml | 6 + .../res/values-or-rIN/smartspace_plurals.xml | 12 + .../res/values-or-rIN/smartspace_strings.xml | 6 + .../res/values-pa-rIN/smartspace_plurals.xml | 12 + .../res/values-pa-rIN/smartspace_strings.xml | 6 + .../res/values-pl-rPL/smartspace_plurals.xml | 16 + .../res/values-pl-rPL/smartspace_strings.xml | 6 + .../res/values-pt-rBR/smartspace_plurals.xml | 12 + .../res/values-pt-rBR/smartspace_strings.xml | 6 + .../res/values-pt-rPT/smartspace_plurals.xml | 12 + .../res/values-pt-rPT/smartspace_strings.xml | 6 + .../res/values-ro-rRO/smartspace_plurals.xml | 14 + .../res/values-ro-rRO/smartspace_strings.xml | 6 + .../res/values-ru-rRU/smartspace_plurals.xml | 16 + .../res/values-ru-rRU/smartspace_strings.xml | 6 + .../res/values-sk-rSK/smartspace_plurals.xml | 16 + .../res/values-sk-rSK/smartspace_strings.xml | 6 + .../res/values-sl-rSI/smartspace_plurals.xml | 16 + .../res/values-sl-rSI/smartspace_strings.xml | 6 + .../res/values-sq-rAL/smartspace_plurals.xml | 12 + .../res/values-sq-rAL/smartspace_strings.xml | 6 + .../res/values-sr-rSP/smartspace_plurals.xml | 14 + .../res/values-sr-rSP/smartspace_strings.xml | 7 + .../res/values-sv-rSE/smartspace_plurals.xml | 12 + .../res/values-sv-rSE/smartspace_strings.xml | 6 + .../res/values-sw-rKE/smartspace_plurals.xml | 12 + .../res/values-sw-rKE/smartspace_strings.xml | 6 + .../res/values-ta-rIN/smartspace_plurals.xml | 12 + .../res/values-ta-rIN/smartspace_strings.xml | 6 + .../res/values-te-rIN/smartspace_plurals.xml | 12 + .../res/values-te-rIN/smartspace_strings.xml | 6 + .../res/values-th-rTH/smartspace_plurals.xml | 10 + .../res/values-th-rTH/smartspace_strings.xml | 6 + .../res/values-tl-rPH/smartspace_plurals.xml | 12 + .../res/values-tl-rPH/smartspace_strings.xml | 6 + .../res/values-tr-rTR/smartspace_plurals.xml | 12 + .../res/values-tr-rTR/smartspace_strings.xml | 6 + .../res/values-uk-rUA/smartspace_plurals.xml | 16 + .../res/values-uk-rUA/smartspace_strings.xml | 6 + .../res/values-uz-rUZ/smartspace_plurals.xml | 12 + .../res/values-uz-rUZ/smartspace_strings.xml | 6 + .../res/values-vi-rVN/smartspace_plurals.xml | 10 + .../res/values-vi-rVN/smartspace_strings.xml | 6 + .../res/values-zh-rCN/smartspace_plurals.xml | 10 + .../res/values-zh-rCN/smartspace_strings.xml | 6 + .../res/values-zh-rHK/smartspace_plurals.xml | 10 + .../res/values-zh-rHK/smartspace_strings.xml | 6 + .../res/values-zh-rSG/smartspace_plurals.xml | 10 + .../res/values-zh-rSG/smartspace_strings.xml | 6 + .../res/values-zh-rTW/smartspace_plurals.xml | 10 + .../res/values-zh-rTW/smartspace_strings.xml | 6 + .../res/values-zu-rZA/smartspace_plurals.xml | 12 + .../res/values-zu-rZA/smartspace_strings.xml | 6 + packages/SystemUI/res/values/config.xml | 2 +- .../SystemUI/res/values/smartspace_dimens.xml | 3 + .../res/values/smartspace_plurals.xml | 10 + .../res/values/smartspace_strings.xml | 5 + .../systemui/SystemUIDefaultModule.java | 4 - .../systemui/SystemUIRootComponent.java | 5 +- ...tificationLockscreenUserManagerGoogle.java | 26 ++ .../systemui/SystemUIGoogleModule.java | 19 + .../keyguard/KeyguardSliceProviderGoogle.java | 184 ++++++++++ .../systemui/smartspace/GSAIntents.java | 19 + .../systemui/smartspace/NewCardInfo.java | 114 ++++++ .../systemui/smartspace/ProtoStore.java | 69 ++++ .../SmartSpaceBroadcastReceiver.java | 85 +++++ .../systemui/smartspace/SmartSpaceCard.java | 329 +++++++++++++++++ .../smartspace/SmartSpaceController.java | 340 ++++++++++++++++++ .../systemui/smartspace/SmartSpaceData.java | 95 +++++ .../smartspace/SmartSpaceUpdateListener.java | 11 + .../systemui/smartspace/smartspace.proto | 92 +++++ .../statusbar/phone/StatusBarGoogle.java | 31 ++ 167 files changed, 2791 insertions(+), 7 deletions(-) create mode 100644 packages/SystemUI/res/values-af-rZA/smartspace_plurals.xml create mode 100644 packages/SystemUI/res/values-af-rZA/smartspace_strings.xml create mode 100644 packages/SystemUI/res/values-am-rET/smartspace_plurals.xml create mode 100644 packages/SystemUI/res/values-am-rET/smartspace_strings.xml create mode 100644 packages/SystemUI/res/values-ar-rSA/smartspace_plurals.xml create mode 100644 packages/SystemUI/res/values-ar-rSA/smartspace_strings.xml create mode 100644 packages/SystemUI/res/values-as-rIN/smartspace_plurals.xml create mode 100644 packages/SystemUI/res/values-as-rIN/smartspace_strings.xml create mode 100644 packages/SystemUI/res/values-az-rAZ/smartspace_plurals.xml create mode 100644 packages/SystemUI/res/values-az-rAZ/smartspace_strings.xml create mode 100644 packages/SystemUI/res/values-be-rBY/smartspace_plurals.xml create mode 100644 packages/SystemUI/res/values-be-rBY/smartspace_strings.xml create mode 100644 packages/SystemUI/res/values-bg-rBG/smartspace_plurals.xml create mode 100644 packages/SystemUI/res/values-bg-rBG/smartspace_strings.xml create mode 100644 packages/SystemUI/res/values-bn-rBD/smartspace_plurals.xml create mode 100644 packages/SystemUI/res/values-bn-rBD/smartspace_strings.xml create mode 100644 packages/SystemUI/res/values-bs-rBA/smartspace_plurals.xml create mode 100644 packages/SystemUI/res/values-bs-rBA/smartspace_strings.xml create mode 100644 packages/SystemUI/res/values-ca-rES/smartspace_plurals.xml create mode 100644 packages/SystemUI/res/values-ca-rES/smartspace_strings.xml create mode 100644 packages/SystemUI/res/values-cs-rCZ/smartspace_plurals.xml create mode 100644 packages/SystemUI/res/values-cs-rCZ/smartspace_strings.xml create mode 100644 packages/SystemUI/res/values-da-rDK/smartspace_plurals.xml create mode 100644 packages/SystemUI/res/values-da-rDK/smartspace_strings.xml create mode 100644 packages/SystemUI/res/values-de-rDE/smartspace_plurals.xml create mode 100644 packages/SystemUI/res/values-de-rDE/smartspace_strings.xml create mode 100644 packages/SystemUI/res/values-el-rGR/smartspace_plurals.xml create mode 100644 packages/SystemUI/res/values-el-rGR/smartspace_strings.xml create mode 100644 packages/SystemUI/res/values-es-rES/smartspace_plurals.xml create mode 100644 packages/SystemUI/res/values-es-rES/smartspace_strings.xml create mode 100644 packages/SystemUI/res/values-es-rMX/smartspace_plurals.xml create mode 100644 packages/SystemUI/res/values-es-rMX/smartspace_strings.xml create mode 100644 packages/SystemUI/res/values-et-rEE/smartspace_plurals.xml create mode 100644 packages/SystemUI/res/values-et-rEE/smartspace_strings.xml create mode 100644 packages/SystemUI/res/values-eu-rES/smartspace_plurals.xml create mode 100644 packages/SystemUI/res/values-eu-rES/smartspace_strings.xml create mode 100644 packages/SystemUI/res/values-fa-rIR/smartspace_plurals.xml create mode 100644 packages/SystemUI/res/values-fa-rIR/smartspace_strings.xml create mode 100644 packages/SystemUI/res/values-fi-rFI/smartspace_plurals.xml create mode 100644 packages/SystemUI/res/values-fi-rFI/smartspace_strings.xml create mode 100644 packages/SystemUI/res/values-fr-rFR/smartspace_plurals.xml create mode 100644 packages/SystemUI/res/values-fr-rFR/smartspace_strings.xml create mode 100644 packages/SystemUI/res/values-gl-rES/smartspace_plurals.xml create mode 100644 packages/SystemUI/res/values-gl-rES/smartspace_strings.xml create mode 100644 packages/SystemUI/res/values-gu-rIN/smartspace_plurals.xml create mode 100644 packages/SystemUI/res/values-gu-rIN/smartspace_strings.xml create mode 100644 packages/SystemUI/res/values-hi-rIN/smartspace_plurals.xml create mode 100644 packages/SystemUI/res/values-hi-rIN/smartspace_strings.xml create mode 100644 packages/SystemUI/res/values-hr-rHR/smartspace_plurals.xml create mode 100644 packages/SystemUI/res/values-hr-rHR/smartspace_strings.xml create mode 100644 packages/SystemUI/res/values-hu-rHU/smartspace_plurals.xml create mode 100644 packages/SystemUI/res/values-hu-rHU/smartspace_strings.xml create mode 100644 packages/SystemUI/res/values-hy-rAM/smartspace_plurals.xml create mode 100644 packages/SystemUI/res/values-hy-rAM/smartspace_strings.xml create mode 100644 packages/SystemUI/res/values-in-rID/smartspace_plurals.xml create mode 100644 packages/SystemUI/res/values-is-rIS/smartspace_plurals.xml create mode 100644 packages/SystemUI/res/values-is-rIS/smartspace_strings.xml create mode 100644 packages/SystemUI/res/values-it-rIT/smartspace_plurals.xml create mode 100644 packages/SystemUI/res/values-it-rIT/smartspace_strings.xml create mode 100644 packages/SystemUI/res/values-iw-rIL/smartspace_strings.xml create mode 100644 packages/SystemUI/res/values-ja-rJP/smartspace_plurals.xml create mode 100644 packages/SystemUI/res/values-ja-rJP/smartspace_strings.xml create mode 100644 packages/SystemUI/res/values-ka-rGE/smartspace_plurals.xml create mode 100644 packages/SystemUI/res/values-ka-rGE/smartspace_strings.xml create mode 100644 packages/SystemUI/res/values-kk-rKZ/smartspace_plurals.xml create mode 100644 packages/SystemUI/res/values-kk-rKZ/smartspace_strings.xml create mode 100644 packages/SystemUI/res/values-km-rKH/smartspace_plurals.xml create mode 100644 packages/SystemUI/res/values-km-rKH/smartspace_strings.xml create mode 100644 packages/SystemUI/res/values-kn-rIN/smartspace_plurals.xml create mode 100644 packages/SystemUI/res/values-kn-rIN/smartspace_strings.xml create mode 100644 packages/SystemUI/res/values-ko-rKR/smartspace_plurals.xml create mode 100644 packages/SystemUI/res/values-ko-rKR/smartspace_strings.xml create mode 100644 packages/SystemUI/res/values-ky-rKG/smartspace_plurals.xml create mode 100644 packages/SystemUI/res/values-ky-rKG/smartspace_strings.xml create mode 100644 packages/SystemUI/res/values-lo-rLA/smartspace_plurals.xml create mode 100644 packages/SystemUI/res/values-lo-rLA/smartspace_strings.xml create mode 100644 packages/SystemUI/res/values-lt-rLT/smartspace_plurals.xml create mode 100644 packages/SystemUI/res/values-lt-rLT/smartspace_strings.xml create mode 100644 packages/SystemUI/res/values-lv-rLV/smartspace_plurals.xml create mode 100644 packages/SystemUI/res/values-lv-rLV/smartspace_strings.xml create mode 100644 packages/SystemUI/res/values-mk-rMK/smartspace_plurals.xml create mode 100644 packages/SystemUI/res/values-mk-rMK/smartspace_strings.xml create mode 100644 packages/SystemUI/res/values-mn-rMN/smartspace_plurals.xml create mode 100644 packages/SystemUI/res/values-mn-rMN/smartspace_strings.xml create mode 100644 packages/SystemUI/res/values-mr-rIN/smartspace_plurals.xml create mode 100644 packages/SystemUI/res/values-mr-rIN/smartspace_strings.xml create mode 100644 packages/SystemUI/res/values-ms-rMY/smartspace_plurals.xml create mode 100644 packages/SystemUI/res/values-ms-rMY/smartspace_strings.xml create mode 100644 packages/SystemUI/res/values-my-rMM/smartspace_plurals.xml create mode 100644 packages/SystemUI/res/values-my-rMM/smartspace_strings.xml create mode 100644 packages/SystemUI/res/values-nb-rNO/smartspace_plurals.xml create mode 100644 packages/SystemUI/res/values-nb-rNO/smartspace_strings.xml create mode 100644 packages/SystemUI/res/values-nl-rNL/smartspace_plurals.xml create mode 100644 packages/SystemUI/res/values-nl-rNL/smartspace_strings.xml create mode 100644 packages/SystemUI/res/values-or-rIN/smartspace_plurals.xml create mode 100644 packages/SystemUI/res/values-or-rIN/smartspace_strings.xml create mode 100644 packages/SystemUI/res/values-pa-rIN/smartspace_plurals.xml create mode 100644 packages/SystemUI/res/values-pa-rIN/smartspace_strings.xml create mode 100644 packages/SystemUI/res/values-pl-rPL/smartspace_plurals.xml create mode 100644 packages/SystemUI/res/values-pl-rPL/smartspace_strings.xml create mode 100644 packages/SystemUI/res/values-pt-rBR/smartspace_plurals.xml create mode 100644 packages/SystemUI/res/values-pt-rBR/smartspace_strings.xml create mode 100644 packages/SystemUI/res/values-pt-rPT/smartspace_plurals.xml create mode 100644 packages/SystemUI/res/values-pt-rPT/smartspace_strings.xml create mode 100644 packages/SystemUI/res/values-ro-rRO/smartspace_plurals.xml create mode 100644 packages/SystemUI/res/values-ro-rRO/smartspace_strings.xml create mode 100644 packages/SystemUI/res/values-ru-rRU/smartspace_plurals.xml create mode 100644 packages/SystemUI/res/values-ru-rRU/smartspace_strings.xml create mode 100644 packages/SystemUI/res/values-sk-rSK/smartspace_plurals.xml create mode 100644 packages/SystemUI/res/values-sk-rSK/smartspace_strings.xml create mode 100644 packages/SystemUI/res/values-sl-rSI/smartspace_plurals.xml create mode 100644 packages/SystemUI/res/values-sl-rSI/smartspace_strings.xml create mode 100644 packages/SystemUI/res/values-sq-rAL/smartspace_plurals.xml create mode 100644 packages/SystemUI/res/values-sq-rAL/smartspace_strings.xml create mode 100644 packages/SystemUI/res/values-sr-rSP/smartspace_plurals.xml create mode 100644 packages/SystemUI/res/values-sr-rSP/smartspace_strings.xml create mode 100644 packages/SystemUI/res/values-sv-rSE/smartspace_plurals.xml create mode 100644 packages/SystemUI/res/values-sv-rSE/smartspace_strings.xml create mode 100644 packages/SystemUI/res/values-sw-rKE/smartspace_plurals.xml create mode 100644 packages/SystemUI/res/values-sw-rKE/smartspace_strings.xml create mode 100644 packages/SystemUI/res/values-ta-rIN/smartspace_plurals.xml create mode 100644 packages/SystemUI/res/values-ta-rIN/smartspace_strings.xml create mode 100644 packages/SystemUI/res/values-te-rIN/smartspace_plurals.xml create mode 100644 packages/SystemUI/res/values-te-rIN/smartspace_strings.xml create mode 100644 packages/SystemUI/res/values-th-rTH/smartspace_plurals.xml create mode 100644 packages/SystemUI/res/values-th-rTH/smartspace_strings.xml create mode 100644 packages/SystemUI/res/values-tl-rPH/smartspace_plurals.xml create mode 100644 packages/SystemUI/res/values-tl-rPH/smartspace_strings.xml create mode 100644 packages/SystemUI/res/values-tr-rTR/smartspace_plurals.xml create mode 100644 packages/SystemUI/res/values-tr-rTR/smartspace_strings.xml create mode 100644 packages/SystemUI/res/values-uk-rUA/smartspace_plurals.xml create mode 100644 packages/SystemUI/res/values-uk-rUA/smartspace_strings.xml create mode 100644 packages/SystemUI/res/values-uz-rUZ/smartspace_plurals.xml create mode 100644 packages/SystemUI/res/values-uz-rUZ/smartspace_strings.xml create mode 100644 packages/SystemUI/res/values-vi-rVN/smartspace_plurals.xml create mode 100644 packages/SystemUI/res/values-vi-rVN/smartspace_strings.xml create mode 100644 packages/SystemUI/res/values-zh-rCN/smartspace_plurals.xml create mode 100644 packages/SystemUI/res/values-zh-rCN/smartspace_strings.xml create mode 100644 packages/SystemUI/res/values-zh-rHK/smartspace_plurals.xml create mode 100644 packages/SystemUI/res/values-zh-rHK/smartspace_strings.xml create mode 100644 packages/SystemUI/res/values-zh-rSG/smartspace_plurals.xml create mode 100644 packages/SystemUI/res/values-zh-rSG/smartspace_strings.xml create mode 100644 packages/SystemUI/res/values-zh-rTW/smartspace_plurals.xml create mode 100644 packages/SystemUI/res/values-zh-rTW/smartspace_strings.xml create mode 100644 packages/SystemUI/res/values-zu-rZA/smartspace_plurals.xml create mode 100644 packages/SystemUI/res/values-zu-rZA/smartspace_strings.xml create mode 100644 packages/SystemUI/res/values/smartspace_dimens.xml create mode 100644 packages/SystemUI/res/values/smartspace_plurals.xml create mode 100644 packages/SystemUI/res/values/smartspace_strings.xml create mode 100644 packages/SystemUI/src/com/google/android/systemui/NotificationLockscreenUserManagerGoogle.java create mode 100644 packages/SystemUI/src/com/google/android/systemui/SystemUIGoogleModule.java create mode 100644 packages/SystemUI/src/com/google/android/systemui/keyguard/KeyguardSliceProviderGoogle.java create mode 100644 packages/SystemUI/src/com/google/android/systemui/smartspace/GSAIntents.java create mode 100644 packages/SystemUI/src/com/google/android/systemui/smartspace/NewCardInfo.java create mode 100644 packages/SystemUI/src/com/google/android/systemui/smartspace/ProtoStore.java create mode 100644 packages/SystemUI/src/com/google/android/systemui/smartspace/SmartSpaceBroadcastReceiver.java create mode 100644 packages/SystemUI/src/com/google/android/systemui/smartspace/SmartSpaceCard.java create mode 100644 packages/SystemUI/src/com/google/android/systemui/smartspace/SmartSpaceController.java create mode 100644 packages/SystemUI/src/com/google/android/systemui/smartspace/SmartSpaceData.java create mode 100644 packages/SystemUI/src/com/google/android/systemui/smartspace/SmartSpaceUpdateListener.java create mode 100644 packages/SystemUI/src/com/google/android/systemui/smartspace/smartspace.proto create mode 100644 packages/SystemUI/src/com/google/android/systemui/statusbar/phone/StatusBarGoogle.java diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index 1dedf0e96fe..10fd3bcd42d 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -258,6 +258,12 @@ + + + + + + - + + + diff --git a/packages/SystemUI/proguard.flags b/packages/SystemUI/proguard.flags index 22b0ab7dde4..563a9325039 100644 --- a/packages/SystemUI/proguard.flags +++ b/packages/SystemUI/proguard.flags @@ -35,3 +35,6 @@ *; } -keep class androidx.core.app.CoreComponentFactory + +# Custom rules +-keep class com.google.android.systemui.** { *; } diff --git a/packages/SystemUI/res/values-af-rZA/smartspace_plurals.xml b/packages/SystemUI/res/values-af-rZA/smartspace_plurals.xml new file mode 100644 index 00000000000..dfeef6c6ade --- /dev/null +++ b/packages/SystemUI/res/values-af-rZA/smartspace_plurals.xml @@ -0,0 +1,12 @@ + + + + + %d uur + %d uur + + + %d min. + %d min. + + diff --git a/packages/SystemUI/res/values-af-rZA/smartspace_strings.xml b/packages/SystemUI/res/values-af-rZA/smartspace_strings.xml new file mode 100644 index 00000000000..25f5785c586 --- /dev/null +++ b/packages/SystemUI/res/values-af-rZA/smartspace_strings.xml @@ -0,0 +1,6 @@ + + + + %1$s %2$s + Nou + diff --git a/packages/SystemUI/res/values-am-rET/smartspace_plurals.xml b/packages/SystemUI/res/values-am-rET/smartspace_plurals.xml new file mode 100644 index 00000000000..3d297c0214e --- /dev/null +++ b/packages/SystemUI/res/values-am-rET/smartspace_plurals.xml @@ -0,0 +1,12 @@ + + + + + %d ሰዓ + %d ሰዓ + + + %d ደቂቃ + %d ደቂቃ + + diff --git a/packages/SystemUI/res/values-am-rET/smartspace_strings.xml b/packages/SystemUI/res/values-am-rET/smartspace_strings.xml new file mode 100644 index 00000000000..82120c8b1d5 --- /dev/null +++ b/packages/SystemUI/res/values-am-rET/smartspace_strings.xml @@ -0,0 +1,6 @@ + + + + %1$s %2$s + አሁን + diff --git a/packages/SystemUI/res/values-ar-rSA/smartspace_plurals.xml b/packages/SystemUI/res/values-ar-rSA/smartspace_plurals.xml new file mode 100644 index 00000000000..165f6ce8789 --- /dev/null +++ b/packages/SystemUI/res/values-ar-rSA/smartspace_plurals.xml @@ -0,0 +1,20 @@ + + + + + ‏%d ساعة + ‏ساعة واحدة (%d) + ‏ساعتان (%d) + ‏%d ساعات + ‏%d ساعة + ‏%d ساعة + + + ‏%d دقيقة + ‏دقيقة واحدة (%d) + ‏دقيقتان (%d) + ‏%d دقائق + ‏%d دقيقة + ‏%d دقيقة + + diff --git a/packages/SystemUI/res/values-ar-rSA/smartspace_strings.xml b/packages/SystemUI/res/values-ar-rSA/smartspace_strings.xml new file mode 100644 index 00000000000..65350654b86 --- /dev/null +++ b/packages/SystemUI/res/values-ar-rSA/smartspace_strings.xml @@ -0,0 +1,6 @@ + + + + %1$s %2$s + الآن + diff --git a/packages/SystemUI/res/values-as-rIN/smartspace_plurals.xml b/packages/SystemUI/res/values-as-rIN/smartspace_plurals.xml new file mode 100644 index 00000000000..78bdd526810 --- /dev/null +++ b/packages/SystemUI/res/values-as-rIN/smartspace_plurals.xml @@ -0,0 +1,12 @@ + + + + + %d ঘ. + %d ঘ. + + + %d মি. + %d মি. + + diff --git a/packages/SystemUI/res/values-as-rIN/smartspace_strings.xml b/packages/SystemUI/res/values-as-rIN/smartspace_strings.xml new file mode 100644 index 00000000000..61d50b96cd3 --- /dev/null +++ b/packages/SystemUI/res/values-as-rIN/smartspace_strings.xml @@ -0,0 +1,6 @@ + + + + %1$s %2$s + এতিয়া + diff --git a/packages/SystemUI/res/values-az-rAZ/smartspace_plurals.xml b/packages/SystemUI/res/values-az-rAZ/smartspace_plurals.xml new file mode 100644 index 00000000000..0ed05017b9e --- /dev/null +++ b/packages/SystemUI/res/values-az-rAZ/smartspace_plurals.xml @@ -0,0 +1,12 @@ + + + + + %d saat + %d saat + + + %d dəq + %d dəq + + diff --git a/packages/SystemUI/res/values-az-rAZ/smartspace_strings.xml b/packages/SystemUI/res/values-az-rAZ/smartspace_strings.xml new file mode 100644 index 00000000000..79395b9e66d --- /dev/null +++ b/packages/SystemUI/res/values-az-rAZ/smartspace_strings.xml @@ -0,0 +1,6 @@ + + + + %1$s %2$s + İndi + diff --git a/packages/SystemUI/res/values-be-rBY/smartspace_plurals.xml b/packages/SystemUI/res/values-be-rBY/smartspace_plurals.xml new file mode 100644 index 00000000000..eb76c8b4c01 --- /dev/null +++ b/packages/SystemUI/res/values-be-rBY/smartspace_plurals.xml @@ -0,0 +1,16 @@ + + + + + %d гадз + %d гадз + %d гадз + %d гадз + + + %d хв + %d хв + %d хв + %d хв + + diff --git a/packages/SystemUI/res/values-be-rBY/smartspace_strings.xml b/packages/SystemUI/res/values-be-rBY/smartspace_strings.xml new file mode 100644 index 00000000000..3a0e098c1fc --- /dev/null +++ b/packages/SystemUI/res/values-be-rBY/smartspace_strings.xml @@ -0,0 +1,6 @@ + + + + %1$s %2$s + Зараз + diff --git a/packages/SystemUI/res/values-bg-rBG/smartspace_plurals.xml b/packages/SystemUI/res/values-bg-rBG/smartspace_plurals.xml new file mode 100644 index 00000000000..6529dc5fa79 --- /dev/null +++ b/packages/SystemUI/res/values-bg-rBG/smartspace_plurals.xml @@ -0,0 +1,12 @@ + + + + + %d ч + %d ч + + + %d мин + %d мин + + diff --git a/packages/SystemUI/res/values-bg-rBG/smartspace_strings.xml b/packages/SystemUI/res/values-bg-rBG/smartspace_strings.xml new file mode 100644 index 00000000000..ef518dfed15 --- /dev/null +++ b/packages/SystemUI/res/values-bg-rBG/smartspace_strings.xml @@ -0,0 +1,6 @@ + + + + %1$s и %2$s + Сега + diff --git a/packages/SystemUI/res/values-bn-rBD/smartspace_plurals.xml b/packages/SystemUI/res/values-bn-rBD/smartspace_plurals.xml new file mode 100644 index 00000000000..2cf1892dcd4 --- /dev/null +++ b/packages/SystemUI/res/values-bn-rBD/smartspace_plurals.xml @@ -0,0 +1,12 @@ + + + + + %d ঘণ্টা + %d ঘণ্টা + + + %d মিনিট + %d মিনিট + + diff --git a/packages/SystemUI/res/values-bn-rBD/smartspace_strings.xml b/packages/SystemUI/res/values-bn-rBD/smartspace_strings.xml new file mode 100644 index 00000000000..e8f8515062b --- /dev/null +++ b/packages/SystemUI/res/values-bn-rBD/smartspace_strings.xml @@ -0,0 +1,6 @@ + + + + %1$s %2$s + এখন + diff --git a/packages/SystemUI/res/values-bs-rBA/smartspace_plurals.xml b/packages/SystemUI/res/values-bs-rBA/smartspace_plurals.xml new file mode 100644 index 00000000000..3bb976fa885 --- /dev/null +++ b/packages/SystemUI/res/values-bs-rBA/smartspace_plurals.xml @@ -0,0 +1,14 @@ + + + + + %d h + %d h + %d h + + + %d min + %d min + %d min + + diff --git a/packages/SystemUI/res/values-bs-rBA/smartspace_strings.xml b/packages/SystemUI/res/values-bs-rBA/smartspace_strings.xml new file mode 100644 index 00000000000..cc48f5355d4 --- /dev/null +++ b/packages/SystemUI/res/values-bs-rBA/smartspace_strings.xml @@ -0,0 +1,6 @@ + + + + %1$s %2$s + Sada + diff --git a/packages/SystemUI/res/values-ca-rES/smartspace_plurals.xml b/packages/SystemUI/res/values-ca-rES/smartspace_plurals.xml new file mode 100644 index 00000000000..8a97f3f31fe --- /dev/null +++ b/packages/SystemUI/res/values-ca-rES/smartspace_plurals.xml @@ -0,0 +1,12 @@ + + + + + %d h + %d h + + + %d min + %d min + + diff --git a/packages/SystemUI/res/values-ca-rES/smartspace_strings.xml b/packages/SystemUI/res/values-ca-rES/smartspace_strings.xml new file mode 100644 index 00000000000..2c41ab00fa6 --- /dev/null +++ b/packages/SystemUI/res/values-ca-rES/smartspace_strings.xml @@ -0,0 +1,6 @@ + + + + %1$s %2$s + Ara + diff --git a/packages/SystemUI/res/values-cs-rCZ/smartspace_plurals.xml b/packages/SystemUI/res/values-cs-rCZ/smartspace_plurals.xml new file mode 100644 index 00000000000..6723aee869c --- /dev/null +++ b/packages/SystemUI/res/values-cs-rCZ/smartspace_plurals.xml @@ -0,0 +1,16 @@ + + + + + %d h + %d h + %d h + %d h + + + %d min + %d min + %d min + %d min + + diff --git a/packages/SystemUI/res/values-cs-rCZ/smartspace_strings.xml b/packages/SystemUI/res/values-cs-rCZ/smartspace_strings.xml new file mode 100644 index 00000000000..39dcb42e93d --- /dev/null +++ b/packages/SystemUI/res/values-cs-rCZ/smartspace_strings.xml @@ -0,0 +1,6 @@ + + + + %1$s %2$s + Právě teď + diff --git a/packages/SystemUI/res/values-da-rDK/smartspace_plurals.xml b/packages/SystemUI/res/values-da-rDK/smartspace_plurals.xml new file mode 100644 index 00000000000..58718145b74 --- /dev/null +++ b/packages/SystemUI/res/values-da-rDK/smartspace_plurals.xml @@ -0,0 +1,12 @@ + + + + + %d t. + %d t. + + + %d min. + %d min. + + diff --git a/packages/SystemUI/res/values-da-rDK/smartspace_strings.xml b/packages/SystemUI/res/values-da-rDK/smartspace_strings.xml new file mode 100644 index 00000000000..2a0f8431c10 --- /dev/null +++ b/packages/SystemUI/res/values-da-rDK/smartspace_strings.xml @@ -0,0 +1,6 @@ + + + + %1$s og %2$s + Nu + diff --git a/packages/SystemUI/res/values-de-rDE/smartspace_plurals.xml b/packages/SystemUI/res/values-de-rDE/smartspace_plurals.xml new file mode 100644 index 00000000000..8a97f3f31fe --- /dev/null +++ b/packages/SystemUI/res/values-de-rDE/smartspace_plurals.xml @@ -0,0 +1,12 @@ + + + + + %d h + %d h + + + %d min + %d min + + diff --git a/packages/SystemUI/res/values-de-rDE/smartspace_strings.xml b/packages/SystemUI/res/values-de-rDE/smartspace_strings.xml new file mode 100644 index 00000000000..1b6b992f207 --- /dev/null +++ b/packages/SystemUI/res/values-de-rDE/smartspace_strings.xml @@ -0,0 +1,6 @@ + + + + %1$s %2$s + Jetzt + diff --git a/packages/SystemUI/res/values-el-rGR/smartspace_plurals.xml b/packages/SystemUI/res/values-el-rGR/smartspace_plurals.xml new file mode 100644 index 00000000000..146fa950706 --- /dev/null +++ b/packages/SystemUI/res/values-el-rGR/smartspace_plurals.xml @@ -0,0 +1,12 @@ + + + + + %d ώ. + %d ώ. + + + %d λ. + %d λ. + + diff --git a/packages/SystemUI/res/values-el-rGR/smartspace_strings.xml b/packages/SystemUI/res/values-el-rGR/smartspace_strings.xml new file mode 100644 index 00000000000..187278da01d --- /dev/null +++ b/packages/SystemUI/res/values-el-rGR/smartspace_strings.xml @@ -0,0 +1,6 @@ + + + + %1$s %2$s + Τώρα + diff --git a/packages/SystemUI/res/values-es-rES/smartspace_plurals.xml b/packages/SystemUI/res/values-es-rES/smartspace_plurals.xml new file mode 100644 index 00000000000..8a97f3f31fe --- /dev/null +++ b/packages/SystemUI/res/values-es-rES/smartspace_plurals.xml @@ -0,0 +1,12 @@ + + + + + %d h + %d h + + + %d min + %d min + + diff --git a/packages/SystemUI/res/values-es-rES/smartspace_strings.xml b/packages/SystemUI/res/values-es-rES/smartspace_strings.xml new file mode 100644 index 00000000000..a7041fecc63 --- /dev/null +++ b/packages/SystemUI/res/values-es-rES/smartspace_strings.xml @@ -0,0 +1,6 @@ + + + + %1$s y %2$s + Ahora + diff --git a/packages/SystemUI/res/values-es-rMX/smartspace_plurals.xml b/packages/SystemUI/res/values-es-rMX/smartspace_plurals.xml new file mode 100644 index 00000000000..8a97f3f31fe --- /dev/null +++ b/packages/SystemUI/res/values-es-rMX/smartspace_plurals.xml @@ -0,0 +1,12 @@ + + + + + %d h + %d h + + + %d min + %d min + + diff --git a/packages/SystemUI/res/values-es-rMX/smartspace_strings.xml b/packages/SystemUI/res/values-es-rMX/smartspace_strings.xml new file mode 100644 index 00000000000..a7041fecc63 --- /dev/null +++ b/packages/SystemUI/res/values-es-rMX/smartspace_strings.xml @@ -0,0 +1,6 @@ + + + + %1$s y %2$s + Ahora + diff --git a/packages/SystemUI/res/values-et-rEE/smartspace_plurals.xml b/packages/SystemUI/res/values-et-rEE/smartspace_plurals.xml new file mode 100644 index 00000000000..8597a44c759 --- /dev/null +++ b/packages/SystemUI/res/values-et-rEE/smartspace_plurals.xml @@ -0,0 +1,12 @@ + + + + + %d h + %d h + + + %d min + %d min + + diff --git a/packages/SystemUI/res/values-et-rEE/smartspace_strings.xml b/packages/SystemUI/res/values-et-rEE/smartspace_strings.xml new file mode 100644 index 00000000000..45c6dee7ac9 --- /dev/null +++ b/packages/SystemUI/res/values-et-rEE/smartspace_strings.xml @@ -0,0 +1,6 @@ + + + + %1$s %2$s + Kohe + diff --git a/packages/SystemUI/res/values-eu-rES/smartspace_plurals.xml b/packages/SystemUI/res/values-eu-rES/smartspace_plurals.xml new file mode 100644 index 00000000000..8a97f3f31fe --- /dev/null +++ b/packages/SystemUI/res/values-eu-rES/smartspace_plurals.xml @@ -0,0 +1,12 @@ + + + + + %d h + %d h + + + %d min + %d min + + diff --git a/packages/SystemUI/res/values-eu-rES/smartspace_strings.xml b/packages/SystemUI/res/values-eu-rES/smartspace_strings.xml new file mode 100644 index 00000000000..1578792ff0a --- /dev/null +++ b/packages/SystemUI/res/values-eu-rES/smartspace_strings.xml @@ -0,0 +1,6 @@ + + + + %1$s eta %2$s + Orain + diff --git a/packages/SystemUI/res/values-fa-rIR/smartspace_plurals.xml b/packages/SystemUI/res/values-fa-rIR/smartspace_plurals.xml new file mode 100644 index 00000000000..c27322c8aa1 --- /dev/null +++ b/packages/SystemUI/res/values-fa-rIR/smartspace_plurals.xml @@ -0,0 +1,12 @@ + + + + + ‏%d ساعت + ‏%d ساعت + + + ‏%d دقیقه + ‏%d دقیقه + + diff --git a/packages/SystemUI/res/values-fa-rIR/smartspace_strings.xml b/packages/SystemUI/res/values-fa-rIR/smartspace_strings.xml new file mode 100644 index 00000000000..03db327fd7a --- /dev/null +++ b/packages/SystemUI/res/values-fa-rIR/smartspace_strings.xml @@ -0,0 +1,6 @@ + + + + %1$s %2$s + ابھی + diff --git a/packages/SystemUI/res/values-fi-rFI/smartspace_plurals.xml b/packages/SystemUI/res/values-fi-rFI/smartspace_plurals.xml new file mode 100644 index 00000000000..8597a44c759 --- /dev/null +++ b/packages/SystemUI/res/values-fi-rFI/smartspace_plurals.xml @@ -0,0 +1,12 @@ + + + + + %d h + %d h + + + %d min + %d min + + diff --git a/packages/SystemUI/res/values-fi-rFI/smartspace_strings.xml b/packages/SystemUI/res/values-fi-rFI/smartspace_strings.xml new file mode 100644 index 00000000000..3908f4baa1f --- /dev/null +++ b/packages/SystemUI/res/values-fi-rFI/smartspace_strings.xml @@ -0,0 +1,6 @@ + + + + %1$s %2$s + Nyt + diff --git a/packages/SystemUI/res/values-fr-rFR/smartspace_plurals.xml b/packages/SystemUI/res/values-fr-rFR/smartspace_plurals.xml new file mode 100644 index 00000000000..8a97f3f31fe --- /dev/null +++ b/packages/SystemUI/res/values-fr-rFR/smartspace_plurals.xml @@ -0,0 +1,12 @@ + + + + + %d h + %d h + + + %d min + %d min + + diff --git a/packages/SystemUI/res/values-fr-rFR/smartspace_strings.xml b/packages/SystemUI/res/values-fr-rFR/smartspace_strings.xml new file mode 100644 index 00000000000..cb40ae98bd1 --- /dev/null +++ b/packages/SystemUI/res/values-fr-rFR/smartspace_strings.xml @@ -0,0 +1,6 @@ + + + + %1$s %2$s + À l\'instant + diff --git a/packages/SystemUI/res/values-gl-rES/smartspace_plurals.xml b/packages/SystemUI/res/values-gl-rES/smartspace_plurals.xml new file mode 100644 index 00000000000..8597a44c759 --- /dev/null +++ b/packages/SystemUI/res/values-gl-rES/smartspace_plurals.xml @@ -0,0 +1,12 @@ + + + + + %d h + %d h + + + %d min + %d min + + diff --git a/packages/SystemUI/res/values-gl-rES/smartspace_strings.xml b/packages/SystemUI/res/values-gl-rES/smartspace_strings.xml new file mode 100644 index 00000000000..2238499995e --- /dev/null +++ b/packages/SystemUI/res/values-gl-rES/smartspace_strings.xml @@ -0,0 +1,6 @@ + + + + %1$s %2$s + Agora + diff --git a/packages/SystemUI/res/values-gu-rIN/smartspace_plurals.xml b/packages/SystemUI/res/values-gu-rIN/smartspace_plurals.xml new file mode 100644 index 00000000000..25491ea0b1c --- /dev/null +++ b/packages/SystemUI/res/values-gu-rIN/smartspace_plurals.xml @@ -0,0 +1,12 @@ + + + + + %d કલાક + %d કલાક + + + %d મિનિટ + %d મિનિટ + + diff --git a/packages/SystemUI/res/values-gu-rIN/smartspace_strings.xml b/packages/SystemUI/res/values-gu-rIN/smartspace_strings.xml new file mode 100644 index 00000000000..9865237d4a1 --- /dev/null +++ b/packages/SystemUI/res/values-gu-rIN/smartspace_strings.xml @@ -0,0 +1,6 @@ + + + + %1$s %2$s + હમણાં + diff --git a/packages/SystemUI/res/values-hi-rIN/smartspace_plurals.xml b/packages/SystemUI/res/values-hi-rIN/smartspace_plurals.xml new file mode 100644 index 00000000000..5e5d6efcc54 --- /dev/null +++ b/packages/SystemUI/res/values-hi-rIN/smartspace_plurals.xml @@ -0,0 +1,12 @@ + + + + + %d घंटे + %d घंटे + + + %d मिनट + %d मिनट + + diff --git a/packages/SystemUI/res/values-hi-rIN/smartspace_strings.xml b/packages/SystemUI/res/values-hi-rIN/smartspace_strings.xml new file mode 100644 index 00000000000..140001ddbc5 --- /dev/null +++ b/packages/SystemUI/res/values-hi-rIN/smartspace_strings.xml @@ -0,0 +1,6 @@ + + + + %1$s %2$s + Sekarang + diff --git a/packages/SystemUI/res/values-hr-rHR/smartspace_plurals.xml b/packages/SystemUI/res/values-hr-rHR/smartspace_plurals.xml new file mode 100644 index 00000000000..3bb976fa885 --- /dev/null +++ b/packages/SystemUI/res/values-hr-rHR/smartspace_plurals.xml @@ -0,0 +1,14 @@ + + + + + %d h + %d h + %d h + + + %d min + %d min + %d min + + diff --git a/packages/SystemUI/res/values-hr-rHR/smartspace_strings.xml b/packages/SystemUI/res/values-hr-rHR/smartspace_strings.xml new file mode 100644 index 00000000000..2832770ac00 --- /dev/null +++ b/packages/SystemUI/res/values-hr-rHR/smartspace_strings.xml @@ -0,0 +1,6 @@ + + + + %1$s %2$s + Sad + diff --git a/packages/SystemUI/res/values-hu-rHU/smartspace_plurals.xml b/packages/SystemUI/res/values-hu-rHU/smartspace_plurals.xml new file mode 100644 index 00000000000..a5ad39c7b2e --- /dev/null +++ b/packages/SystemUI/res/values-hu-rHU/smartspace_plurals.xml @@ -0,0 +1,12 @@ + + + + + %d óra + %d óra + + + %d perc + %d perc + + diff --git a/packages/SystemUI/res/values-hu-rHU/smartspace_strings.xml b/packages/SystemUI/res/values-hu-rHU/smartspace_strings.xml new file mode 100644 index 00000000000..5604b7f28ef --- /dev/null +++ b/packages/SystemUI/res/values-hu-rHU/smartspace_strings.xml @@ -0,0 +1,6 @@ + + + + %1$s %2$s + Most + diff --git a/packages/SystemUI/res/values-hy-rAM/smartspace_plurals.xml b/packages/SystemUI/res/values-hy-rAM/smartspace_plurals.xml new file mode 100644 index 00000000000..a47295f6752 --- /dev/null +++ b/packages/SystemUI/res/values-hy-rAM/smartspace_plurals.xml @@ -0,0 +1,12 @@ + + + + + %d ժ + %d ժ + + + %d ր + %d ր + + diff --git a/packages/SystemUI/res/values-hy-rAM/smartspace_strings.xml b/packages/SystemUI/res/values-hy-rAM/smartspace_strings.xml new file mode 100644 index 00000000000..03a8b73c168 --- /dev/null +++ b/packages/SystemUI/res/values-hy-rAM/smartspace_strings.xml @@ -0,0 +1,6 @@ + + + + %1$s %2$s + Հիմա + diff --git a/packages/SystemUI/res/values-in-rID/smartspace_plurals.xml b/packages/SystemUI/res/values-in-rID/smartspace_plurals.xml new file mode 100644 index 00000000000..9419e7ce67a --- /dev/null +++ b/packages/SystemUI/res/values-in-rID/smartspace_plurals.xml @@ -0,0 +1,10 @@ + + + + + %d jam + + + %d mnt + + diff --git a/packages/SystemUI/res/values-is-rIS/smartspace_plurals.xml b/packages/SystemUI/res/values-is-rIS/smartspace_plurals.xml new file mode 100644 index 00000000000..d6570922cdf --- /dev/null +++ b/packages/SystemUI/res/values-is-rIS/smartspace_plurals.xml @@ -0,0 +1,12 @@ + + + + + %d klst. + %d klst. + + + %d mín. + %d mín. + + diff --git a/packages/SystemUI/res/values-is-rIS/smartspace_strings.xml b/packages/SystemUI/res/values-is-rIS/smartspace_strings.xml new file mode 100644 index 00000000000..717e5bbeaa2 --- /dev/null +++ b/packages/SystemUI/res/values-is-rIS/smartspace_strings.xml @@ -0,0 +1,6 @@ + + + + %1$s og %2$s + Núna + diff --git a/packages/SystemUI/res/values-it-rIT/smartspace_plurals.xml b/packages/SystemUI/res/values-it-rIT/smartspace_plurals.xml new file mode 100644 index 00000000000..8597a44c759 --- /dev/null +++ b/packages/SystemUI/res/values-it-rIT/smartspace_plurals.xml @@ -0,0 +1,12 @@ + + + + + %d h + %d h + + + %d min + %d min + + diff --git a/packages/SystemUI/res/values-it-rIT/smartspace_strings.xml b/packages/SystemUI/res/values-it-rIT/smartspace_strings.xml new file mode 100644 index 00000000000..5a784ae48b4 --- /dev/null +++ b/packages/SystemUI/res/values-it-rIT/smartspace_strings.xml @@ -0,0 +1,6 @@ + + + + %1$s %2$s + Ora + diff --git a/packages/SystemUI/res/values-iw-rIL/smartspace_strings.xml b/packages/SystemUI/res/values-iw-rIL/smartspace_strings.xml new file mode 100644 index 00000000000..aee5725b269 --- /dev/null +++ b/packages/SystemUI/res/values-iw-rIL/smartspace_strings.xml @@ -0,0 +1,6 @@ + + + + %1$s ו-%2$s + עכשיו + diff --git a/packages/SystemUI/res/values-ja-rJP/smartspace_plurals.xml b/packages/SystemUI/res/values-ja-rJP/smartspace_plurals.xml new file mode 100644 index 00000000000..2853d12de66 --- /dev/null +++ b/packages/SystemUI/res/values-ja-rJP/smartspace_plurals.xml @@ -0,0 +1,10 @@ + + + + + %d 時間 + + + %d 分 + + diff --git a/packages/SystemUI/res/values-ja-rJP/smartspace_strings.xml b/packages/SystemUI/res/values-ja-rJP/smartspace_strings.xml new file mode 100644 index 00000000000..fd0d6efc953 --- /dev/null +++ b/packages/SystemUI/res/values-ja-rJP/smartspace_strings.xml @@ -0,0 +1,6 @@ + + + + %1$s %2$s + たった今 + diff --git a/packages/SystemUI/res/values-ka-rGE/smartspace_plurals.xml b/packages/SystemUI/res/values-ka-rGE/smartspace_plurals.xml new file mode 100644 index 00000000000..4b2af19a259 --- /dev/null +++ b/packages/SystemUI/res/values-ka-rGE/smartspace_plurals.xml @@ -0,0 +1,12 @@ + + + + + %d სთ + %d სთ + + + %d წთ + %d წთ + + diff --git a/packages/SystemUI/res/values-ka-rGE/smartspace_strings.xml b/packages/SystemUI/res/values-ka-rGE/smartspace_strings.xml new file mode 100644 index 00000000000..f657cb54a92 --- /dev/null +++ b/packages/SystemUI/res/values-ka-rGE/smartspace_strings.xml @@ -0,0 +1,6 @@ + + + + %1$s და %2$s + ახლა + diff --git a/packages/SystemUI/res/values-kk-rKZ/smartspace_plurals.xml b/packages/SystemUI/res/values-kk-rKZ/smartspace_plurals.xml new file mode 100644 index 00000000000..84249a6be1f --- /dev/null +++ b/packages/SystemUI/res/values-kk-rKZ/smartspace_plurals.xml @@ -0,0 +1,12 @@ + + + + + %d сағ + %d сағ + + + %d мин + %d мин + + diff --git a/packages/SystemUI/res/values-kk-rKZ/smartspace_strings.xml b/packages/SystemUI/res/values-kk-rKZ/smartspace_strings.xml new file mode 100644 index 00000000000..f7d3e86b32f --- /dev/null +++ b/packages/SystemUI/res/values-kk-rKZ/smartspace_strings.xml @@ -0,0 +1,6 @@ + + + + %1$s %2$s + Қазір + diff --git a/packages/SystemUI/res/values-km-rKH/smartspace_plurals.xml b/packages/SystemUI/res/values-km-rKH/smartspace_plurals.xml new file mode 100644 index 00000000000..e5adc9d74bd --- /dev/null +++ b/packages/SystemUI/res/values-km-rKH/smartspace_plurals.xml @@ -0,0 +1,10 @@ + + + + + %d ម៉ + + + %d ន + + diff --git a/packages/SystemUI/res/values-km-rKH/smartspace_strings.xml b/packages/SystemUI/res/values-km-rKH/smartspace_strings.xml new file mode 100644 index 00000000000..3072a9f91f8 --- /dev/null +++ b/packages/SystemUI/res/values-km-rKH/smartspace_strings.xml @@ -0,0 +1,6 @@ + + + + %1$s %2$s + ឥឡូវ​នេះ + diff --git a/packages/SystemUI/res/values-kn-rIN/smartspace_plurals.xml b/packages/SystemUI/res/values-kn-rIN/smartspace_plurals.xml new file mode 100644 index 00000000000..aebe512e4c6 --- /dev/null +++ b/packages/SystemUI/res/values-kn-rIN/smartspace_plurals.xml @@ -0,0 +1,12 @@ + + + + + %d ಗಂಟೆ + %d ಗಂಟೆ + + + %d ನಿಮಿಷ + %d ನಿಮಿಷ + + diff --git a/packages/SystemUI/res/values-kn-rIN/smartspace_strings.xml b/packages/SystemUI/res/values-kn-rIN/smartspace_strings.xml new file mode 100644 index 00000000000..8bf2f1fda4a --- /dev/null +++ b/packages/SystemUI/res/values-kn-rIN/smartspace_strings.xml @@ -0,0 +1,6 @@ + + + + %1$s %2$s + ಈಗ + diff --git a/packages/SystemUI/res/values-ko-rKR/smartspace_plurals.xml b/packages/SystemUI/res/values-ko-rKR/smartspace_plurals.xml new file mode 100644 index 00000000000..737fadb8558 --- /dev/null +++ b/packages/SystemUI/res/values-ko-rKR/smartspace_plurals.xml @@ -0,0 +1,10 @@ + + + + + %d시간 + + + %d분 + + diff --git a/packages/SystemUI/res/values-ko-rKR/smartspace_strings.xml b/packages/SystemUI/res/values-ko-rKR/smartspace_strings.xml new file mode 100644 index 00000000000..b594f0e9f07 --- /dev/null +++ b/packages/SystemUI/res/values-ko-rKR/smartspace_strings.xml @@ -0,0 +1,6 @@ + + + + %1$s %2$s + 방금 + diff --git a/packages/SystemUI/res/values-ky-rKG/smartspace_plurals.xml b/packages/SystemUI/res/values-ky-rKG/smartspace_plurals.xml new file mode 100644 index 00000000000..ebed98697db --- /dev/null +++ b/packages/SystemUI/res/values-ky-rKG/smartspace_plurals.xml @@ -0,0 +1,12 @@ + + + + + %d с. + %d с. + + + %d мүн. + %d мүн. + + diff --git a/packages/SystemUI/res/values-ky-rKG/smartspace_strings.xml b/packages/SystemUI/res/values-ky-rKG/smartspace_strings.xml new file mode 100644 index 00000000000..13286f0f683 --- /dev/null +++ b/packages/SystemUI/res/values-ky-rKG/smartspace_strings.xml @@ -0,0 +1,6 @@ + + + + %1$s %2$s + Азыр + diff --git a/packages/SystemUI/res/values-lo-rLA/smartspace_plurals.xml b/packages/SystemUI/res/values-lo-rLA/smartspace_plurals.xml new file mode 100644 index 00000000000..fcfca50f7d6 --- /dev/null +++ b/packages/SystemUI/res/values-lo-rLA/smartspace_plurals.xml @@ -0,0 +1,10 @@ + + + + + %d ຊມ + + + %d ນທ + + diff --git a/packages/SystemUI/res/values-lo-rLA/smartspace_strings.xml b/packages/SystemUI/res/values-lo-rLA/smartspace_strings.xml new file mode 100644 index 00000000000..5293aebe7c1 --- /dev/null +++ b/packages/SystemUI/res/values-lo-rLA/smartspace_strings.xml @@ -0,0 +1,6 @@ + + + + %1$s %2$s + ຕອນນີ້ + diff --git a/packages/SystemUI/res/values-lt-rLT/smartspace_plurals.xml b/packages/SystemUI/res/values-lt-rLT/smartspace_plurals.xml new file mode 100644 index 00000000000..d352553050e --- /dev/null +++ b/packages/SystemUI/res/values-lt-rLT/smartspace_plurals.xml @@ -0,0 +1,16 @@ + + + + + %d valanda + %d valandos + %d valandos + %d valandų + + + %d minutė + %d minutės + %d minutės + %d minučių + + diff --git a/packages/SystemUI/res/values-lt-rLT/smartspace_strings.xml b/packages/SystemUI/res/values-lt-rLT/smartspace_strings.xml new file mode 100644 index 00000000000..6babdbaf789 --- /dev/null +++ b/packages/SystemUI/res/values-lt-rLT/smartspace_strings.xml @@ -0,0 +1,6 @@ + + + + %1$s %2$s + Dabar + diff --git a/packages/SystemUI/res/values-lv-rLV/smartspace_plurals.xml b/packages/SystemUI/res/values-lv-rLV/smartspace_plurals.xml new file mode 100644 index 00000000000..0ef35bac3ec --- /dev/null +++ b/packages/SystemUI/res/values-lv-rLV/smartspace_plurals.xml @@ -0,0 +1,14 @@ + + + + + %d h + %d h + %d h + + + %d min + %d min + %d min + + diff --git a/packages/SystemUI/res/values-lv-rLV/smartspace_strings.xml b/packages/SystemUI/res/values-lv-rLV/smartspace_strings.xml new file mode 100644 index 00000000000..98c57589c66 --- /dev/null +++ b/packages/SystemUI/res/values-lv-rLV/smartspace_strings.xml @@ -0,0 +1,6 @@ + + + + %1$s %2$s + Tikko + diff --git a/packages/SystemUI/res/values-mk-rMK/smartspace_plurals.xml b/packages/SystemUI/res/values-mk-rMK/smartspace_plurals.xml new file mode 100644 index 00000000000..109ea8acb18 --- /dev/null +++ b/packages/SystemUI/res/values-mk-rMK/smartspace_plurals.xml @@ -0,0 +1,12 @@ + + + + + %d час + %d часа + + + %d мин. + %d мин. + + diff --git a/packages/SystemUI/res/values-mk-rMK/smartspace_strings.xml b/packages/SystemUI/res/values-mk-rMK/smartspace_strings.xml new file mode 100644 index 00000000000..e0992621d0e --- /dev/null +++ b/packages/SystemUI/res/values-mk-rMK/smartspace_strings.xml @@ -0,0 +1,6 @@ + + + + %1$s %2$s + Сега + diff --git a/packages/SystemUI/res/values-mn-rMN/smartspace_plurals.xml b/packages/SystemUI/res/values-mn-rMN/smartspace_plurals.xml new file mode 100644 index 00000000000..def071ad7e6 --- /dev/null +++ b/packages/SystemUI/res/values-mn-rMN/smartspace_plurals.xml @@ -0,0 +1,12 @@ + + + + + %d цаг + %d цаг + + + %d мин + %d мин + + diff --git a/packages/SystemUI/res/values-mn-rMN/smartspace_strings.xml b/packages/SystemUI/res/values-mn-rMN/smartspace_strings.xml new file mode 100644 index 00000000000..dd7cc67b30c --- /dev/null +++ b/packages/SystemUI/res/values-mn-rMN/smartspace_strings.xml @@ -0,0 +1,6 @@ + + + + %1$s %2$s + Одоо + diff --git a/packages/SystemUI/res/values-mr-rIN/smartspace_plurals.xml b/packages/SystemUI/res/values-mr-rIN/smartspace_plurals.xml new file mode 100644 index 00000000000..ccd38bdbcf2 --- /dev/null +++ b/packages/SystemUI/res/values-mr-rIN/smartspace_plurals.xml @@ -0,0 +1,12 @@ + + + + + %d ता + %d ता + + + %d मि + %d मि + + diff --git a/packages/SystemUI/res/values-mr-rIN/smartspace_strings.xml b/packages/SystemUI/res/values-mr-rIN/smartspace_strings.xml new file mode 100644 index 00000000000..ce8bd59ee5d --- /dev/null +++ b/packages/SystemUI/res/values-mr-rIN/smartspace_strings.xml @@ -0,0 +1,6 @@ + + + + %1$s %2$s + आता + diff --git a/packages/SystemUI/res/values-ms-rMY/smartspace_plurals.xml b/packages/SystemUI/res/values-ms-rMY/smartspace_plurals.xml new file mode 100644 index 00000000000..5e62e74301a --- /dev/null +++ b/packages/SystemUI/res/values-ms-rMY/smartspace_plurals.xml @@ -0,0 +1,10 @@ + + + + + %d jam + + + %d min + + diff --git a/packages/SystemUI/res/values-ms-rMY/smartspace_strings.xml b/packages/SystemUI/res/values-ms-rMY/smartspace_strings.xml new file mode 100644 index 00000000000..140001ddbc5 --- /dev/null +++ b/packages/SystemUI/res/values-ms-rMY/smartspace_strings.xml @@ -0,0 +1,6 @@ + + + + %1$s %2$s + Sekarang + diff --git a/packages/SystemUI/res/values-my-rMM/smartspace_plurals.xml b/packages/SystemUI/res/values-my-rMM/smartspace_plurals.xml new file mode 100644 index 00000000000..4bd604dc3f4 --- /dev/null +++ b/packages/SystemUI/res/values-my-rMM/smartspace_plurals.xml @@ -0,0 +1,10 @@ + + + + + %d နာရီ + + + %d မိနစ် + + diff --git a/packages/SystemUI/res/values-my-rMM/smartspace_strings.xml b/packages/SystemUI/res/values-my-rMM/smartspace_strings.xml new file mode 100644 index 00000000000..af4905a9c5b --- /dev/null +++ b/packages/SystemUI/res/values-my-rMM/smartspace_strings.xml @@ -0,0 +1,6 @@ + + + + %1$s %2$s + ယခု + diff --git a/packages/SystemUI/res/values-nb-rNO/smartspace_plurals.xml b/packages/SystemUI/res/values-nb-rNO/smartspace_plurals.xml new file mode 100644 index 00000000000..085fc397106 --- /dev/null +++ b/packages/SystemUI/res/values-nb-rNO/smartspace_plurals.xml @@ -0,0 +1,12 @@ + + + + + %d t + %d t + + + %d min + %d min + + diff --git a/packages/SystemUI/res/values-nb-rNO/smartspace_strings.xml b/packages/SystemUI/res/values-nb-rNO/smartspace_strings.xml new file mode 100644 index 00000000000..7bea9b5af30 --- /dev/null +++ b/packages/SystemUI/res/values-nb-rNO/smartspace_strings.xml @@ -0,0 +1,6 @@ + + + + %1$s %2$s + + diff --git a/packages/SystemUI/res/values-nl-rNL/smartspace_plurals.xml b/packages/SystemUI/res/values-nl-rNL/smartspace_plurals.xml new file mode 100644 index 00000000000..25917b0eb1d --- /dev/null +++ b/packages/SystemUI/res/values-nl-rNL/smartspace_plurals.xml @@ -0,0 +1,12 @@ + + + + + %d u + %d u + + + %d min + %d min + + diff --git a/packages/SystemUI/res/values-nl-rNL/smartspace_strings.xml b/packages/SystemUI/res/values-nl-rNL/smartspace_strings.xml new file mode 100644 index 00000000000..7f6ade0636d --- /dev/null +++ b/packages/SystemUI/res/values-nl-rNL/smartspace_strings.xml @@ -0,0 +1,6 @@ + + + + %1$s %2$s + Nu + diff --git a/packages/SystemUI/res/values-or-rIN/smartspace_plurals.xml b/packages/SystemUI/res/values-or-rIN/smartspace_plurals.xml new file mode 100644 index 00000000000..601f4eaf43c --- /dev/null +++ b/packages/SystemUI/res/values-or-rIN/smartspace_plurals.xml @@ -0,0 +1,12 @@ + + + + + %d ଘଣ୍ଟା + %d ଘଣ୍ଟା + + + %d ମିନିଟ୍‍ + %d ମିନିଟ୍‍ + + diff --git a/packages/SystemUI/res/values-or-rIN/smartspace_strings.xml b/packages/SystemUI/res/values-or-rIN/smartspace_strings.xml new file mode 100644 index 00000000000..4977a5f23bd --- /dev/null +++ b/packages/SystemUI/res/values-or-rIN/smartspace_strings.xml @@ -0,0 +1,6 @@ + + + + %1$s %2$s + ବର୍ତ୍ତମାନ + diff --git a/packages/SystemUI/res/values-pa-rIN/smartspace_plurals.xml b/packages/SystemUI/res/values-pa-rIN/smartspace_plurals.xml new file mode 100644 index 00000000000..be68198fd21 --- /dev/null +++ b/packages/SystemUI/res/values-pa-rIN/smartspace_plurals.xml @@ -0,0 +1,12 @@ + + + + + %d ਘੰਟਾ + %d ਘੰਟੇ + + + %d ਮਿੰਟ + %d ਮਿੰਟ + + diff --git a/packages/SystemUI/res/values-pa-rIN/smartspace_strings.xml b/packages/SystemUI/res/values-pa-rIN/smartspace_strings.xml new file mode 100644 index 00000000000..2d50186d104 --- /dev/null +++ b/packages/SystemUI/res/values-pa-rIN/smartspace_strings.xml @@ -0,0 +1,6 @@ + + + + %1$s %2$s + ਹੁਣੇ + diff --git a/packages/SystemUI/res/values-pl-rPL/smartspace_plurals.xml b/packages/SystemUI/res/values-pl-rPL/smartspace_plurals.xml new file mode 100644 index 00000000000..903dbfb4be7 --- /dev/null +++ b/packages/SystemUI/res/values-pl-rPL/smartspace_plurals.xml @@ -0,0 +1,16 @@ + + + + + %d godz. + %d godz. + %d godz. + %d godz. + + + %d min + %d min + %d min + %d min + + diff --git a/packages/SystemUI/res/values-pl-rPL/smartspace_strings.xml b/packages/SystemUI/res/values-pl-rPL/smartspace_strings.xml new file mode 100644 index 00000000000..4e1fad59441 --- /dev/null +++ b/packages/SystemUI/res/values-pl-rPL/smartspace_strings.xml @@ -0,0 +1,6 @@ + + + + %1$s %2$s + Teraz + diff --git a/packages/SystemUI/res/values-pt-rBR/smartspace_plurals.xml b/packages/SystemUI/res/values-pt-rBR/smartspace_plurals.xml new file mode 100644 index 00000000000..8597a44c759 --- /dev/null +++ b/packages/SystemUI/res/values-pt-rBR/smartspace_plurals.xml @@ -0,0 +1,12 @@ + + + + + %d h + %d h + + + %d min + %d min + + diff --git a/packages/SystemUI/res/values-pt-rBR/smartspace_strings.xml b/packages/SystemUI/res/values-pt-rBR/smartspace_strings.xml new file mode 100644 index 00000000000..4d228347097 --- /dev/null +++ b/packages/SystemUI/res/values-pt-rBR/smartspace_strings.xml @@ -0,0 +1,6 @@ + + + + %1$s e %2$s + Agora + diff --git a/packages/SystemUI/res/values-pt-rPT/smartspace_plurals.xml b/packages/SystemUI/res/values-pt-rPT/smartspace_plurals.xml new file mode 100644 index 00000000000..8597a44c759 --- /dev/null +++ b/packages/SystemUI/res/values-pt-rPT/smartspace_plurals.xml @@ -0,0 +1,12 @@ + + + + + %d h + %d h + + + %d min + %d min + + diff --git a/packages/SystemUI/res/values-pt-rPT/smartspace_strings.xml b/packages/SystemUI/res/values-pt-rPT/smartspace_strings.xml new file mode 100644 index 00000000000..4d228347097 --- /dev/null +++ b/packages/SystemUI/res/values-pt-rPT/smartspace_strings.xml @@ -0,0 +1,6 @@ + + + + %1$s e %2$s + Agora + diff --git a/packages/SystemUI/res/values-ro-rRO/smartspace_plurals.xml b/packages/SystemUI/res/values-ro-rRO/smartspace_plurals.xml new file mode 100644 index 00000000000..3bb976fa885 --- /dev/null +++ b/packages/SystemUI/res/values-ro-rRO/smartspace_plurals.xml @@ -0,0 +1,14 @@ + + + + + %d h + %d h + %d h + + + %d min + %d min + %d min + + diff --git a/packages/SystemUI/res/values-ro-rRO/smartspace_strings.xml b/packages/SystemUI/res/values-ro-rRO/smartspace_strings.xml new file mode 100644 index 00000000000..07089b3efc8 --- /dev/null +++ b/packages/SystemUI/res/values-ro-rRO/smartspace_strings.xml @@ -0,0 +1,6 @@ + + + + %1$s %2$s + Acum + diff --git a/packages/SystemUI/res/values-ru-rRU/smartspace_plurals.xml b/packages/SystemUI/res/values-ru-rRU/smartspace_plurals.xml new file mode 100644 index 00000000000..fe30cb2caa1 --- /dev/null +++ b/packages/SystemUI/res/values-ru-rRU/smartspace_plurals.xml @@ -0,0 +1,16 @@ + + + + + %d ч. + %d ч. + %d ч. + %d ч. + + + %d мин. + %d мин. + %d мин. + %d мин. + + diff --git a/packages/SystemUI/res/values-ru-rRU/smartspace_strings.xml b/packages/SystemUI/res/values-ru-rRU/smartspace_strings.xml new file mode 100644 index 00000000000..08d5dc28572 --- /dev/null +++ b/packages/SystemUI/res/values-ru-rRU/smartspace_strings.xml @@ -0,0 +1,6 @@ + + + + %1$s %2$s + Только что + diff --git a/packages/SystemUI/res/values-sk-rSK/smartspace_plurals.xml b/packages/SystemUI/res/values-sk-rSK/smartspace_plurals.xml new file mode 100644 index 00000000000..6723aee869c --- /dev/null +++ b/packages/SystemUI/res/values-sk-rSK/smartspace_plurals.xml @@ -0,0 +1,16 @@ + + + + + %d h + %d h + %d h + %d h + + + %d min + %d min + %d min + %d min + + diff --git a/packages/SystemUI/res/values-sk-rSK/smartspace_strings.xml b/packages/SystemUI/res/values-sk-rSK/smartspace_strings.xml new file mode 100644 index 00000000000..22380f98a7a --- /dev/null +++ b/packages/SystemUI/res/values-sk-rSK/smartspace_strings.xml @@ -0,0 +1,6 @@ + + + + %1$s %2$s + Teraz + diff --git a/packages/SystemUI/res/values-sl-rSI/smartspace_plurals.xml b/packages/SystemUI/res/values-sl-rSI/smartspace_plurals.xml new file mode 100644 index 00000000000..24cb4bb52b9 --- /dev/null +++ b/packages/SystemUI/res/values-sl-rSI/smartspace_plurals.xml @@ -0,0 +1,16 @@ + + + + + %d h + %d h + %d h + %d h + + + %d min + %d min + %d min + %d min + + diff --git a/packages/SystemUI/res/values-sl-rSI/smartspace_strings.xml b/packages/SystemUI/res/values-sl-rSI/smartspace_strings.xml new file mode 100644 index 00000000000..ee663aa7af9 --- /dev/null +++ b/packages/SystemUI/res/values-sl-rSI/smartspace_strings.xml @@ -0,0 +1,6 @@ + + + + %1$s %2$s + Zdaj + diff --git a/packages/SystemUI/res/values-sq-rAL/smartspace_plurals.xml b/packages/SystemUI/res/values-sq-rAL/smartspace_plurals.xml new file mode 100644 index 00000000000..14bec8f0388 --- /dev/null +++ b/packages/SystemUI/res/values-sq-rAL/smartspace_plurals.xml @@ -0,0 +1,12 @@ + + + + + %d orë + %d orë + + + %d min. + %d min. + + diff --git a/packages/SystemUI/res/values-sq-rAL/smartspace_strings.xml b/packages/SystemUI/res/values-sq-rAL/smartspace_strings.xml new file mode 100644 index 00000000000..c55b4df35a8 --- /dev/null +++ b/packages/SystemUI/res/values-sq-rAL/smartspace_strings.xml @@ -0,0 +1,6 @@ + + + + %1$s %2$s + Tani + diff --git a/packages/SystemUI/res/values-sr-rSP/smartspace_plurals.xml b/packages/SystemUI/res/values-sr-rSP/smartspace_plurals.xml new file mode 100644 index 00000000000..70d4bba0010 --- /dev/null +++ b/packages/SystemUI/res/values-sr-rSP/smartspace_plurals.xml @@ -0,0 +1,14 @@ + + + + + %d ч + %d ч + %d ч + + + %d мин + %d мин + %d мин + + diff --git a/packages/SystemUI/res/values-sr-rSP/smartspace_strings.xml b/packages/SystemUI/res/values-sr-rSP/smartspace_strings.xml new file mode 100644 index 00000000000..d8cff2b94a7 --- /dev/null +++ b/packages/SystemUI/res/values-sr-rSP/smartspace_strings.xml @@ -0,0 +1,7 @@ + + + + %1$s %2$s + Сада + %1$s %2$s + diff --git a/packages/SystemUI/res/values-sv-rSE/smartspace_plurals.xml b/packages/SystemUI/res/values-sv-rSE/smartspace_plurals.xml new file mode 100644 index 00000000000..49d67b20602 --- /dev/null +++ b/packages/SystemUI/res/values-sv-rSE/smartspace_plurals.xml @@ -0,0 +1,12 @@ + + + + + %d tim + %d tim + + + %d min + %d min + + diff --git a/packages/SystemUI/res/values-sv-rSE/smartspace_strings.xml b/packages/SystemUI/res/values-sv-rSE/smartspace_strings.xml new file mode 100644 index 00000000000..7f6ade0636d --- /dev/null +++ b/packages/SystemUI/res/values-sv-rSE/smartspace_strings.xml @@ -0,0 +1,6 @@ + + + + %1$s %2$s + Nu + diff --git a/packages/SystemUI/res/values-sw-rKE/smartspace_plurals.xml b/packages/SystemUI/res/values-sw-rKE/smartspace_plurals.xml new file mode 100644 index 00000000000..30bd83fced8 --- /dev/null +++ b/packages/SystemUI/res/values-sw-rKE/smartspace_plurals.xml @@ -0,0 +1,12 @@ + + + + + saa %d + saa %d + + + dakika %d + dakika %d + + diff --git a/packages/SystemUI/res/values-sw-rKE/smartspace_strings.xml b/packages/SystemUI/res/values-sw-rKE/smartspace_strings.xml new file mode 100644 index 00000000000..7b4487eda1d --- /dev/null +++ b/packages/SystemUI/res/values-sw-rKE/smartspace_strings.xml @@ -0,0 +1,6 @@ + + + + %1$s %2$s + Sasa + diff --git a/packages/SystemUI/res/values-ta-rIN/smartspace_plurals.xml b/packages/SystemUI/res/values-ta-rIN/smartspace_plurals.xml new file mode 100644 index 00000000000..04f91eacc9e --- /dev/null +++ b/packages/SystemUI/res/values-ta-rIN/smartspace_plurals.xml @@ -0,0 +1,12 @@ + + + + + %d ம.நே. + %d ம.நே. + + + %d நிமி. + %d நிமி. + + diff --git a/packages/SystemUI/res/values-ta-rIN/smartspace_strings.xml b/packages/SystemUI/res/values-ta-rIN/smartspace_strings.xml new file mode 100644 index 00000000000..1fc8d512eba --- /dev/null +++ b/packages/SystemUI/res/values-ta-rIN/smartspace_strings.xml @@ -0,0 +1,6 @@ + + + + %1$s %2$s + இப்போது + diff --git a/packages/SystemUI/res/values-te-rIN/smartspace_plurals.xml b/packages/SystemUI/res/values-te-rIN/smartspace_plurals.xml new file mode 100644 index 00000000000..bf57043af64 --- /dev/null +++ b/packages/SystemUI/res/values-te-rIN/smartspace_plurals.xml @@ -0,0 +1,12 @@ + + + + + %d గం + %d గం + + + %d నిమి + %d నిమి + + diff --git a/packages/SystemUI/res/values-te-rIN/smartspace_strings.xml b/packages/SystemUI/res/values-te-rIN/smartspace_strings.xml new file mode 100644 index 00000000000..a52a8a697d4 --- /dev/null +++ b/packages/SystemUI/res/values-te-rIN/smartspace_strings.xml @@ -0,0 +1,6 @@ + + + + %1$s %2$s + ఇప్పుడే + diff --git a/packages/SystemUI/res/values-th-rTH/smartspace_plurals.xml b/packages/SystemUI/res/values-th-rTH/smartspace_plurals.xml new file mode 100644 index 00000000000..8497e5b7c5a --- /dev/null +++ b/packages/SystemUI/res/values-th-rTH/smartspace_plurals.xml @@ -0,0 +1,10 @@ + + + + + %d ชม. + + + %d นาที + + diff --git a/packages/SystemUI/res/values-th-rTH/smartspace_strings.xml b/packages/SystemUI/res/values-th-rTH/smartspace_strings.xml new file mode 100644 index 00000000000..0f825bbaa9f --- /dev/null +++ b/packages/SystemUI/res/values-th-rTH/smartspace_strings.xml @@ -0,0 +1,6 @@ + + + + %1$s %2$s + ตอนนี้ + diff --git a/packages/SystemUI/res/values-tl-rPH/smartspace_plurals.xml b/packages/SystemUI/res/values-tl-rPH/smartspace_plurals.xml new file mode 100644 index 00000000000..149b609acc8 --- /dev/null +++ b/packages/SystemUI/res/values-tl-rPH/smartspace_plurals.xml @@ -0,0 +1,12 @@ + + + + + %d oras + %d na oras + + + %d minuto + %d na minuto + + diff --git a/packages/SystemUI/res/values-tl-rPH/smartspace_strings.xml b/packages/SystemUI/res/values-tl-rPH/smartspace_strings.xml new file mode 100644 index 00000000000..b0f85608b0d --- /dev/null +++ b/packages/SystemUI/res/values-tl-rPH/smartspace_strings.xml @@ -0,0 +1,6 @@ + + + + %1$s %2$s + Ngayon + diff --git a/packages/SystemUI/res/values-tr-rTR/smartspace_plurals.xml b/packages/SystemUI/res/values-tr-rTR/smartspace_plurals.xml new file mode 100644 index 00000000000..8a4a94c7aa7 --- /dev/null +++ b/packages/SystemUI/res/values-tr-rTR/smartspace_plurals.xml @@ -0,0 +1,12 @@ + + + + + %d sa. + %d sa. + + + %d dk. + %d dk. + + diff --git a/packages/SystemUI/res/values-tr-rTR/smartspace_strings.xml b/packages/SystemUI/res/values-tr-rTR/smartspace_strings.xml new file mode 100644 index 00000000000..814c6e9f8a6 --- /dev/null +++ b/packages/SystemUI/res/values-tr-rTR/smartspace_strings.xml @@ -0,0 +1,6 @@ + + + + %1$s %2$s + Şimdi + diff --git a/packages/SystemUI/res/values-uk-rUA/smartspace_plurals.xml b/packages/SystemUI/res/values-uk-rUA/smartspace_plurals.xml new file mode 100644 index 00000000000..346f284a6ad --- /dev/null +++ b/packages/SystemUI/res/values-uk-rUA/smartspace_plurals.xml @@ -0,0 +1,16 @@ + + + + + %d год + %d год + %d год + %d год + + + %d хв + %d хв + %d хв + %d хв + + diff --git a/packages/SystemUI/res/values-uk-rUA/smartspace_strings.xml b/packages/SystemUI/res/values-uk-rUA/smartspace_strings.xml new file mode 100644 index 00000000000..c34a97f70fd --- /dev/null +++ b/packages/SystemUI/res/values-uk-rUA/smartspace_strings.xml @@ -0,0 +1,6 @@ + + + + %1$s %2$s + Щойно + diff --git a/packages/SystemUI/res/values-uz-rUZ/smartspace_plurals.xml b/packages/SystemUI/res/values-uz-rUZ/smartspace_plurals.xml new file mode 100644 index 00000000000..ba6080a4b72 --- /dev/null +++ b/packages/SystemUI/res/values-uz-rUZ/smartspace_plurals.xml @@ -0,0 +1,12 @@ + + + + + %d soat + %d soat + + + %d daqiqa + %d daqiqa + + diff --git a/packages/SystemUI/res/values-uz-rUZ/smartspace_strings.xml b/packages/SystemUI/res/values-uz-rUZ/smartspace_strings.xml new file mode 100644 index 00000000000..db855f567d8 --- /dev/null +++ b/packages/SystemUI/res/values-uz-rUZ/smartspace_strings.xml @@ -0,0 +1,6 @@ + + + + %1$s %2$s + Hozir + diff --git a/packages/SystemUI/res/values-vi-rVN/smartspace_plurals.xml b/packages/SystemUI/res/values-vi-rVN/smartspace_plurals.xml new file mode 100644 index 00000000000..edd4a9aa98b --- /dev/null +++ b/packages/SystemUI/res/values-vi-rVN/smartspace_plurals.xml @@ -0,0 +1,10 @@ + + + + + %d giờ + + + %d phút + + diff --git a/packages/SystemUI/res/values-vi-rVN/smartspace_strings.xml b/packages/SystemUI/res/values-vi-rVN/smartspace_strings.xml new file mode 100644 index 00000000000..58f579ac6bc --- /dev/null +++ b/packages/SystemUI/res/values-vi-rVN/smartspace_strings.xml @@ -0,0 +1,6 @@ + + + + %1$s %2$s + Hiện tại + diff --git a/packages/SystemUI/res/values-zh-rCN/smartspace_plurals.xml b/packages/SystemUI/res/values-zh-rCN/smartspace_plurals.xml new file mode 100644 index 00000000000..4646c2ae5d7 --- /dev/null +++ b/packages/SystemUI/res/values-zh-rCN/smartspace_plurals.xml @@ -0,0 +1,10 @@ + + + + + %d 小时 + + + %d 分钟 + + diff --git a/packages/SystemUI/res/values-zh-rCN/smartspace_strings.xml b/packages/SystemUI/res/values-zh-rCN/smartspace_strings.xml new file mode 100644 index 00000000000..7eb51a8f57b --- /dev/null +++ b/packages/SystemUI/res/values-zh-rCN/smartspace_strings.xml @@ -0,0 +1,6 @@ + + + + %1$s %2$s + 现在 + diff --git a/packages/SystemUI/res/values-zh-rHK/smartspace_plurals.xml b/packages/SystemUI/res/values-zh-rHK/smartspace_plurals.xml new file mode 100644 index 00000000000..e0517f52ad0 --- /dev/null +++ b/packages/SystemUI/res/values-zh-rHK/smartspace_plurals.xml @@ -0,0 +1,10 @@ + + + + + %d 小時 + + + %d 分鐘 + + diff --git a/packages/SystemUI/res/values-zh-rHK/smartspace_strings.xml b/packages/SystemUI/res/values-zh-rHK/smartspace_strings.xml new file mode 100644 index 00000000000..d8518d19b90 --- /dev/null +++ b/packages/SystemUI/res/values-zh-rHK/smartspace_strings.xml @@ -0,0 +1,6 @@ + + + + %1$s %2$s + 剛剛 + diff --git a/packages/SystemUI/res/values-zh-rSG/smartspace_plurals.xml b/packages/SystemUI/res/values-zh-rSG/smartspace_plurals.xml new file mode 100644 index 00000000000..e0517f52ad0 --- /dev/null +++ b/packages/SystemUI/res/values-zh-rSG/smartspace_plurals.xml @@ -0,0 +1,10 @@ + + + + + %d 小時 + + + %d 分鐘 + + diff --git a/packages/SystemUI/res/values-zh-rSG/smartspace_strings.xml b/packages/SystemUI/res/values-zh-rSG/smartspace_strings.xml new file mode 100644 index 00000000000..be332bc8cc6 --- /dev/null +++ b/packages/SystemUI/res/values-zh-rSG/smartspace_strings.xml @@ -0,0 +1,6 @@ + + + + %1$s %2$s + 現在 + diff --git a/packages/SystemUI/res/values-zh-rTW/smartspace_plurals.xml b/packages/SystemUI/res/values-zh-rTW/smartspace_plurals.xml new file mode 100644 index 00000000000..e0517f52ad0 --- /dev/null +++ b/packages/SystemUI/res/values-zh-rTW/smartspace_plurals.xml @@ -0,0 +1,10 @@ + + + + + %d 小時 + + + %d 分鐘 + + diff --git a/packages/SystemUI/res/values-zh-rTW/smartspace_strings.xml b/packages/SystemUI/res/values-zh-rTW/smartspace_strings.xml new file mode 100644 index 00000000000..be332bc8cc6 --- /dev/null +++ b/packages/SystemUI/res/values-zh-rTW/smartspace_strings.xml @@ -0,0 +1,6 @@ + + + + %1$s %2$s + 現在 + diff --git a/packages/SystemUI/res/values-zu-rZA/smartspace_plurals.xml b/packages/SystemUI/res/values-zu-rZA/smartspace_plurals.xml new file mode 100644 index 00000000000..417fa97bbc8 --- /dev/null +++ b/packages/SystemUI/res/values-zu-rZA/smartspace_plurals.xml @@ -0,0 +1,12 @@ + + + + + %d ihora + %d ihora + + + %d iminithi + %d iminithi + + diff --git a/packages/SystemUI/res/values-zu-rZA/smartspace_strings.xml b/packages/SystemUI/res/values-zu-rZA/smartspace_strings.xml new file mode 100644 index 00000000000..3e88ae05123 --- /dev/null +++ b/packages/SystemUI/res/values-zu-rZA/smartspace_strings.xml @@ -0,0 +1,6 @@ + + + + %1$s %2$s + Manje + diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index e851e81bc47..d44f9b8eda0 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -30,7 +30,7 @@ - com.android.systemui.statusbar.phone.StatusBar + com.google.android.systemui.statusbar.phone.StatusBarGoogle + + + + + + + + + + diff --git a/packages/SystemUI/res/drawable/ic_qs_reboot_recovery.xml b/packages/SystemUI/res/drawable/ic_qs_reboot_recovery.xml new file mode 100644 index 00000000000..29328460a19 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_qs_reboot_recovery.xml @@ -0,0 +1,54 @@ + + + + + + + + + diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index d44f9b8eda0..351e5bf6bcc 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -117,7 +117,7 @@ - wifi,cell,battery,dnd,flashlight,rotation,bt,airplane,location,hotspot,nfc,inversion,saver,dark,work,cast,night,caffeine,heads_up,sync,screenshot + wifi,cell,battery,dnd,flashlight,rotation,bt,airplane,location,hotspot,nfc,inversion,saver,dark,work,cast,night,caffeine,heads_up,sync,screenshot,reboot diff --git a/packages/SystemUI/res/values/reloaded_strings.xml b/packages/SystemUI/res/values/reloaded_strings.xml index fbc6084b40c..55bdf295fad 100644 --- a/packages/SystemUI/res/values/reloaded_strings.xml +++ b/packages/SystemUI/res/values/reloaded_strings.xml @@ -128,4 +128,8 @@ Auto hide Hide the traffic monitor when there is no activity + + Reboot + Recovery + 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 7ab032e7979..1a27a682fee 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java @@ -42,6 +42,7 @@ import com.android.systemui.qs.tiles.LocationTile; import com.android.systemui.qs.tiles.NfcTile; import com.android.systemui.qs.tiles.NightDisplayTile; +import com.android.systemui.qs.tiles.RebootTile; import com.android.systemui.qs.tiles.RotationLockTile; import com.android.systemui.qs.tiles.SyncTile; import com.android.systemui.qs.tiles.ScreenshotTile; @@ -194,6 +195,8 @@ private QSTileImpl createTileInternal(String tileSpec) { return mSyncTileProvider.get(); case "screenshot": return mScreenshotTileProvider.get(); + case "reboot": + return new RebootTile(mHost); } // Intent tiles. diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RebootTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/RebootTile.java new file mode 100644 index 00000000000..f85175ffc2c --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RebootTile.java @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2013 Slimroms + * + * 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.os.Handler; +import android.os.PowerManager; + +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.QSTile.BooleanState; +import com.android.systemui.qs.QSHost; +import com.android.systemui.qs.tileimpl.QSTileImpl; + +public class RebootTile extends QSTileImpl { + + private boolean mRebootToRecovery = false; + + public RebootTile(QSHost host) { + super(host); + } + + @Override + public BooleanState newTileState() { + return new BooleanState(); + } + + @Override + public void handleClick() { + mRebootToRecovery = !mRebootToRecovery; + refreshState(); + } + + @Override + protected void handleLongClick() { + mHost.collapsePanels(); + Handler handler = new Handler(); + handler.postDelayed(new Runnable() { + public void run() { + PowerManager pm = + (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); + pm.reboot(mRebootToRecovery ? "recovery" : ""); + } + }, 500); + } + + @Override + public Intent getLongClickIntent() { + return null; + } + + @Override + public CharSequence getTileLabel() { + return mContext.getString(R.string.quick_settings_reboot_label); + } + + @Override + public int getMetricsCategory() { + return MetricsEvent.RELOADED; + } + + @Override + protected void handleUpdateState(BooleanState state, Object arg) { + if (mRebootToRecovery) { + state.label = mContext.getString(R.string.quick_settings_reboot_recovery_label); + state.icon = ResourceIcon.get(R.drawable.ic_qs_reboot_recovery); + } else { + state.label = mContext.getString(R.string.quick_settings_reboot_label); + state.icon = ResourceIcon.get(R.drawable.ic_qs_reboot); + } + } + + @Override + public void handleSetListening(boolean listening) { + } +} From c4e2c347642cbd0da4f2746ebc1b7fa376551708 Mon Sep 17 00:00:00 2001 From: LuK1337 Date: Tue, 25 Aug 2020 15:10:54 +0200 Subject: [PATCH 09/26] PackageInstaller: Fix crash when uninstalling apps with fragile data * Fixes: - https://gitlab.com/LineageOS/issues/android/-/issues/2298 - https://issuetracker.google.com/issues/148573843 Change-Id: I5a27d6f02d17a52e1a1a2aff4fb46327ce5e6127 --- .../UninstallAlertDialogFragment.java | 28 ++++++------------- 1 file changed, 8 insertions(+), 20 deletions(-) diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/handheld/UninstallAlertDialogFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/handheld/UninstallAlertDialogFragment.java index 0a37cc6cb6c..678a0adf218 100644 --- a/packages/PackageInstaller/src/com/android/packageinstaller/handheld/UninstallAlertDialogFragment.java +++ b/packages/PackageInstaller/src/com/android/packageinstaller/handheld/UninstallAlertDialogFragment.java @@ -35,7 +35,6 @@ import android.os.UserHandle; import android.os.UserManager; import android.os.storage.StorageManager; -import android.os.storage.StorageVolume; import android.util.Log; import android.view.LayoutInflater; import android.view.View; @@ -64,29 +63,18 @@ public class UninstallAlertDialogFragment extends DialogFragment implements * @return The number of bytes. */ private long getAppDataSizeForUser(@NonNull String pkg, @NonNull UserHandle user) { - StorageManager storageManager = getContext().getSystemService(StorageManager.class); + PackageManager packageManager = getContext().getPackageManager(); StorageStatsManager storageStatsManager = getContext().getSystemService(StorageStatsManager.class); - List volumes = storageManager.getStorageVolumes(); - long appDataSize = 0; - - int numVolumes = volumes.size(); - for (int i = 0; i < numVolumes; i++) { - StorageStats stats; - try { - stats = storageStatsManager.queryStatsForPackage(convert(volumes.get(i).getUuid()), - pkg, user); - } catch (PackageManager.NameNotFoundException | IOException e) { - Log.e(LOG_TAG, "Cannot determine amount of app data for " + pkg + " on " - + volumes.get(i) + " (user " + user + ")", e); - continue; - } - - appDataSize += stats.getDataBytes(); + try { + ApplicationInfo info = packageManager.getApplicationInfo(pkg, 0); + return storageStatsManager.queryStatsForPackage( + info.storageUuid, pkg, user).getDataBytes(); + } catch (PackageManager.NameNotFoundException | IOException e) { + Log.e(LOG_TAG, "Cannot determine amount of app data for " + pkg, e); + return 0; } - - return appDataSize; } /** From 038edc72c4b40bc6c4150eea506a5fafecbaed88 Mon Sep 17 00:00:00 2001 From: rituj Date: Mon, 11 May 2020 21:17:54 +0530 Subject: [PATCH 10/26] base: Use 2 modalities concurrently in BiometricPrompt if available * Add a new dialog view to incorporate both Face and Fingerprint icons * Example: Face and fingerprint Signed-off-by: rituj Change-Id: Idaf47f4fbf062a865f0c7a02fd495fdb7133578e --- .../internal/statusbar/IStatusBar.aidl | 2 +- .../internal/statusbar/IStatusBarService.aidl | 2 +- .../biometrics/BiometricDialogImpl.java | 48 ++- .../biometrics/BiometricDialogView.java | 15 +- .../FingerprintAndFaceDialogView.java | 274 ++++++++++++++++++ .../systemui/statusbar/CommandQueue.java | 8 +- .../server/biometrics/BiometricService.java | 49 ++-- .../statusbar/StatusBarManagerService.java | 4 +- 8 files changed, 359 insertions(+), 43 deletions(-) create mode 100644 packages/SystemUI/src/com/android/systemui/biometrics/FingerprintAndFaceDialogView.java diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl index 482e5961c1e..84ed49bbea2 100644 --- a/core/java/com/android/internal/statusbar/IStatusBar.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl @@ -155,7 +155,7 @@ oneway interface IStatusBar void showBiometricDialog(in Bundle bundle, IBiometricServiceReceiverInternal receiver, int type, boolean requireConfirmation, int userId); // Used to hide the dialog when a biometric is authenticated - void onBiometricAuthenticated(boolean authenticated, String failureReason); + void onBiometricAuthenticated(boolean authenticated, String failureReason, boolean requireConfirmation); // Used to set a temporary message, e.g. fingerprint not recognized, finger moved too fast, etc void onBiometricHelp(String message); // Used to set a message - the dialog will dismiss after a certain amount of time diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl index 5921f64abed..cf97da1d221 100644 --- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl @@ -103,7 +103,7 @@ interface IStatusBarService void showBiometricDialog(in Bundle bundle, IBiometricServiceReceiverInternal receiver, int type, boolean requireConfirmation, int userId); // Used to hide the dialog when a biometric is authenticated - void onBiometricAuthenticated(boolean authenticated, String failureReason); + void onBiometricAuthenticated(boolean authenticated, String failureReason, boolean requireConfirmation); // Used to set a temporary message, e.g. fingerprint not recognized, finger moved too fast, etc void onBiometricHelp(String message); // Used to set a message - the dialog will dismiss after a certain amount of time diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java index e66a8fa9629..c66a01cb94a 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java @@ -59,6 +59,7 @@ public class BiometricDialogImpl extends SystemUI implements CommandQueue.Callba private WindowManager mWindowManager; private IBiometricServiceReceiverInternal mReceiver; private boolean mDialogShowing; + private boolean mTryAgainPressed; private Callback mCallback = new Callback(); private WakefulnessLifecycle mWakefulnessLifecycle; @@ -73,7 +74,8 @@ public void handleMessage(Message msg) { case MSG_BIOMETRIC_AUTHENTICATED: { SomeArgs args = (SomeArgs) msg.obj; handleBiometricAuthenticated((boolean) args.arg1 /* authenticated */, - (String) args.arg2 /* failureReason */); + (String) args.arg2 /* failureReason */, + (boolean) args.arg3); args.recycle(); break; } @@ -181,13 +183,14 @@ public void showBiometricDialog(Bundle bundle, IBiometricServiceReceiverInternal } @Override - public void onBiometricAuthenticated(boolean authenticated, String failureReason) { + public void onBiometricAuthenticated(boolean authenticated, String failureReason, boolean requireConfirmation) { if (DEBUG) Log.d(TAG, "onBiometricAuthenticated: " + authenticated + " reason: " + failureReason); SomeArgs args = SomeArgs.obtain(); args.arg1 = authenticated; args.arg2 = failureReason; + args.arg3 = requireConfirmation; mHandler.obtainMessage(MSG_BIOMETRIC_AUTHENTICATED, args).sendToTarget(); } @@ -216,14 +219,20 @@ private void handleShowDialog(SomeArgs args, boolean skipAnimation, Bundle saved final int type = args.argi1; // Create a new dialog but do not replace the current one yet. - BiometricDialogView newDialog; - if (type == BiometricAuthenticator.TYPE_FINGERPRINT) { - newDialog = new FingerprintDialogView(mContext, mCallback); - } else if (type == BiometricAuthenticator.TYPE_FACE) { - newDialog = new FaceDialogView(mContext, mCallback); - } else { - Log.e(TAG, "Unsupported type: " + type); - return; + BiometricDialogView newDialog = mCurrentDialog; + final boolean isFingerprint = (type & BiometricAuthenticator.TYPE_FINGERPRINT) != 0; + final boolean isFace = (type & BiometricAuthenticator.TYPE_FACE) != 0; + if (!mTryAgainPressed) { + if (isFace && isFingerprint) { + newDialog = new FingerprintAndFaceDialogView(mContext, mCallback); + } else if (isFingerprint) { + newDialog = new FingerprintDialogView(mContext, mCallback); + } else if (isFace) { + newDialog = new FaceDialogView(mContext, mCallback); + } else { + Log.e(TAG, "Unsupported type: " + type); + return; + } } if (DEBUG) Log.d(TAG, "handleShowDialog, " @@ -236,32 +245,38 @@ private void handleShowDialog(SomeArgs args, boolean skipAnimation, Bundle saved // SavedState is only non-null if it's from onConfigurationChanged. Restore the state // even though it may be removed / re-created again newDialog.restoreState(savedState); - } else if (mCurrentDialog != null && mDialogShowing) { + } else if (mCurrentDialog != null && mDialogShowing && !mTryAgainPressed) { // If somehow we're asked to show a dialog, the old one doesn't need to be animated // away. This can happen if the app cancels and re-starts auth during configuration // change. This is ugly because we also have to do things on onConfigurationChanged // here. mCurrentDialog.forceRemove(); + mDialogShowing = false; } + newDialog.setFaceAndFingerprint(isFace, isFingerprint); mReceiver = (IBiometricServiceReceiverInternal) args.arg2; newDialog.setBundle((Bundle) args.arg1); newDialog.setRequireConfirmation((boolean) args.arg3); newDialog.setUserId(args.argi2); newDialog.setSkipIntro(skipAnimation); mCurrentDialog = newDialog; - mWindowManager.addView(mCurrentDialog, mCurrentDialog.getLayoutParams()); + if (!mTryAgainPressed && !mDialogShowing) { + mWindowManager.addView(mCurrentDialog, mCurrentDialog.getLayoutParams()); + } mDialogShowing = true; + mTryAgainPressed = false; } - private void handleBiometricAuthenticated(boolean authenticated, String failureReason) { - if (DEBUG) Log.d(TAG, "handleBiometricAuthenticated: " + authenticated); + private void handleBiometricAuthenticated(boolean authenticated, String failureReason, boolean requireConfirmation) { + if (DEBUG) Log.d(TAG, "handleBiometricAuthenticated: " + authenticated + + " requireConfirmation: " + requireConfirmation); if (authenticated) { mCurrentDialog.announceForAccessibility( mContext.getResources() .getText(mCurrentDialog.getAuthenticatedAccessibilityResourceId())); - if (mCurrentDialog.requiresConfirmation()) { + if (requireConfirmation) { mCurrentDialog.updateState(BiometricDialogView.STATE_PENDING_CONFIRMATION); } else { mCurrentDialog.updateState(BiometricDialogView.STATE_AUTHENTICATED); @@ -305,6 +320,7 @@ private void handleHideDialog(boolean userCanceled) { } mReceiver = null; mDialogShowing = false; + mTryAgainPressed = false; mCurrentDialog.startDismiss(); } @@ -340,6 +356,7 @@ private void handleUserCanceled() { private void handleTryAgainPressed() { try { + mTryAgainPressed = true; mReceiver.onTryAgainPressed(); } catch (RemoteException e) { Log.e(TAG, "RemoteException when handling try again", e); @@ -360,6 +377,7 @@ protected void onConfigurationChanged(Configuration newConfig) { if (mDialogShowing) { mCurrentDialog.forceRemove(); mDialogShowing = false; + mTryAgainPressed = false; } if (wasShowing) { diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java index cbbd3a0b056..4f14739f7ed 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java @@ -108,6 +108,8 @@ public abstract class BiometricDialogView extends LinearLayout { private boolean mAnimatingAway; private boolean mWasForceRemoved; private boolean mSkipIntro; + private boolean mIsFingerprint; + private boolean mIsFace; protected boolean mRequireConfirmation; private int mUserId; // used to determine if we should show work background @@ -346,6 +348,15 @@ public void onAttachedToWindow() { mSkipIntro = false; } + protected int getAnimatingAwayDuration() { + return ANIMATION_DURATION_AWAY; + } + + public void setFaceAndFingerprint(boolean isFace, boolean isFingerprint) { + mIsFace = isFace; + mIsFingerprint = isFingerprint; + } + private void setDismissesDialog(View v) { v.setClickable(true); v.setOnClickListener(v1 -> { @@ -382,13 +393,13 @@ public void run() { public void run() { mLayout.animate() .alpha(0f) - .setDuration(ANIMATION_DURATION_AWAY) + .setDuration(getAnimatingAwayDuration()) .setInterpolator(mLinearOutSlowIn) .withLayer() .start(); mDialog.animate() .translationY(mAnimationTranslationOffset) - .setDuration(ANIMATION_DURATION_AWAY) + .setDuration(getAnimatingAwayDuration()) .setInterpolator(mLinearOutSlowIn) .withLayer() .withEndAction(endActionRunnable) diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/FingerprintAndFaceDialogView.java b/packages/SystemUI/src/com/android/systemui/biometrics/FingerprintAndFaceDialogView.java new file mode 100644 index 00000000000..a7da4947de3 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/biometrics/FingerprintAndFaceDialogView.java @@ -0,0 +1,274 @@ +/* + * 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.biometrics; + +import static android.view.Gravity.CENTER_HORIZONTAL; +import static android.view.Gravity.TOP; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.AnimatorSet; +import android.animation.ValueAnimator; +import android.content.Context; +import android.graphics.Outline; +import android.graphics.drawable.Animatable2; +import android.graphics.drawable.AnimatedVectorDrawable; +import android.graphics.drawable.Drawable; +import android.hardware.biometrics.BiometricPrompt; +import android.os.Bundle; +import android.util.Log; +import android.view.View; +import android.widget.ImageView; +import android.widget.FrameLayout; + +import com.android.systemui.R; + +/** + * This class loads the view for the system-provided dialog. The view consists of: + * Application Icon, Title, Subtitle, Description, Biometric Icon, Error/Help message area, + * and positive/negative buttons. + */ +public class FingerprintAndFaceDialogView extends BiometricDialogView { + + private static final String TAG = "FingerprintAndFaceDialogView"; + private static final String KEY_DIALOG_ANIMATED_IN = "key_dialog_animated_in"; + + private static final int HIDE_DIALOG_DELAY = 200; // ms + + private IconController mIconController; + private ImageView mFaceIcon; + private boolean mDialogAnimatedIn; + + /** + * Class that handles the biometric icon animations. + */ + private final class IconController extends Animatable2.AnimationCallback { + + private boolean mLastPulseDirection; // false = dark to light, true = light to dark + + int mState; + + IconController() { + mState = STATE_IDLE; + } + + public void animateOnce(int iconRes) { + animateIcon(iconRes, false); + } + + public void showStatic(int iconRes) { + mFaceIcon.setImageDrawable(mContext.getDrawable(iconRes)); + } + + public void startPulsing() { + mLastPulseDirection = false; + animateIcon(R.drawable.face_dialog_pulse_dark_to_light, true); + } + + public void showIcon(int iconRes) { + final Drawable drawable = mContext.getDrawable(iconRes); + mFaceIcon.setImageDrawable(drawable); + } + + private void animateIcon(int iconRes, boolean repeat) { + final AnimatedVectorDrawable icon = + (AnimatedVectorDrawable) mContext.getDrawable(iconRes); + mFaceIcon.setImageDrawable(icon); + icon.forceAnimationOnUI(); + if (repeat) { + icon.registerAnimationCallback(this); + } + icon.start(); + } + + private void pulseInNextDirection() { + int iconRes = mLastPulseDirection ? R.drawable.face_dialog_pulse_dark_to_light + : R.drawable.face_dialog_pulse_light_to_dark; + animateIcon(iconRes, true /* repeat */); + mLastPulseDirection = !mLastPulseDirection; + } + + @Override + public void onAnimationEnd(Drawable drawable) { + super.onAnimationEnd(drawable); + + if (mState == STATE_AUTHENTICATING) { + // Still authenticating, pulse the icon + pulseInNextDirection(); + } + } + } + + private final Runnable mErrorToIdleAnimationRunnable = () -> { + updateState(STATE_IDLE); + announceAccessibilityEvent(); + }; + + public FingerprintAndFaceDialogView(Context context, + DialogViewCallback callback) { + super(context, callback); + + mIconController = new IconController(); + mFaceIcon = new ImageView(context); + final int iconDim = getResources().getDimensionPixelSize( + R.dimen.biometric_dialog_biometric_icon_size); + mFaceIcon.setVisibility(View.VISIBLE); + mLayout.addView(mFaceIcon); + FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mFaceIcon.getLayoutParams(); + lp.gravity = TOP | CENTER_HORIZONTAL; + lp.width = iconDim; + lp.height = iconDim; + lp.topMargin = iconDim; + } + + @Override + public void onSaveState(Bundle bundle) { + super.onSaveState(bundle); + bundle.putBoolean(KEY_DIALOG_ANIMATED_IN, mDialogAnimatedIn); + } + + @Override + protected void handleResetMessage() { + mErrorText.setText(getHintStringResourceId()); + mErrorText.setTextColor(mTextColor); + announceAccessibilityEvent(); + } + + @Override + public void restoreState(Bundle bundle) { + super.restoreState(bundle); + // Keep in mind that this happens before onAttachedToWindow() + mDialogAnimatedIn = bundle.getBoolean(KEY_DIALOG_ANIMATED_IN); + } + + @Override + public void onAuthenticationFailed(String message) { + super.onAuthenticationFailed(message); + showTryAgainButton(true); + } + + @Override + public void showTryAgainButton(boolean show) { + if (show) { + mTryAgainButton.setVisibility(View.VISIBLE); + mPositiveButton.setVisibility(View.GONE); + announceAccessibilityEvent(); + } else { + mTryAgainButton.setVisibility(View.GONE); + announceAccessibilityEvent(); + } + } + + @Override + protected int getHintStringResourceId() { + return R.string.fingerprint_dialog_touch_sensor; + } + + @Override + protected int getAuthenticatedAccessibilityResourceId() { + return com.android.internal.R.string.fingerprint_authenticated; + } + + @Override + protected int getIconDescriptionResourceId() { + return R.string.accessibility_fingerprint_dialog_fingerprint_icon; + } + + @Override + protected void updateIcon(int oldState, int newState) { + mIconController.mState = newState; + + if (newState == STATE_AUTHENTICATING) { + mHandler.removeCallbacks(mErrorToIdleAnimationRunnable); + if (mDialogAnimatedIn) { + mIconController.startPulsing(); + } else { + mIconController.showIcon(R.drawable.face_dialog_pulse_dark_to_light); + } + mFaceIcon.setContentDescription(mContext.getString( + R.string.biometric_dialog_face_icon_description_authenticating)); + } else if (oldState == STATE_PENDING_CONFIRMATION && newState == STATE_AUTHENTICATED) { + mIconController.animateOnce(R.drawable.face_dialog_dark_to_checkmark); + mFaceIcon.setContentDescription(mContext.getString( + R.string.biometric_dialog_face_icon_description_confirmed)); + } else if (oldState == STATE_ERROR && newState == STATE_IDLE) { + mIconController.animateOnce(R.drawable.face_dialog_error_to_idle); + mFaceIcon.setContentDescription(mContext.getString( + R.string.biometric_dialog_face_icon_description_idle)); + } else if (oldState == STATE_ERROR && newState == STATE_AUTHENTICATED) { + mHandler.removeCallbacks(mErrorToIdleAnimationRunnable); + mIconController.animateOnce(R.drawable.face_dialog_dark_to_checkmark); + mFaceIcon.setContentDescription(mContext.getString( + R.string.biometric_dialog_face_icon_description_authenticated)); + } else if (newState == STATE_ERROR) { + // It's easier to only check newState and gate showing the animation on the + // mErrorToIdleAnimationRunnable as a proxy, than add a ton of extra state. For example, + // we may go from error -> error due to configuration change which is valid and we + // should show the animation, or we can go from error -> error by receiving repeated + // acquire messages in which case we do not want to repeatedly start the animation. + if (!mHandler.hasCallbacks(mErrorToIdleAnimationRunnable)) { + mIconController.animateOnce(R.drawable.face_dialog_dark_to_error); + mHandler.postDelayed(mErrorToIdleAnimationRunnable, + BiometricPrompt.HIDE_DIALOG_DELAY); + } + } else if (oldState == STATE_AUTHENTICATING && newState == STATE_AUTHENTICATED) { + mIconController.animateOnce(R.drawable.face_dialog_dark_to_checkmark); + mFaceIcon.setContentDescription(mContext.getString( + R.string.biometric_dialog_face_icon_description_authenticated)); + } else if (newState == STATE_PENDING_CONFIRMATION) { + mHandler.removeCallbacks(mErrorToIdleAnimationRunnable); + mIconController.animateOnce(R.drawable.face_dialog_wink_from_dark); + mFaceIcon.setContentDescription(mContext.getString( + R.string.biometric_dialog_face_icon_description_authenticated)); + } else if (newState == STATE_IDLE) { + mIconController.showStatic(R.drawable.face_dialog_idle_static); + mFaceIcon.setContentDescription(mContext.getString( + R.string.biometric_dialog_face_icon_description_idle)); + } else { + Log.w(TAG, "Unknown animation from " + oldState + " -> " + newState); + } + + final Drawable icon = mContext.getDrawable(R.drawable.fingerprint_dialog_fp_to_error); + mBiometricIcon.setImageDrawable(icon); + + // Note that this must be after the newState == STATE_ERROR check above since this affects + // the logic. + if (oldState == STATE_ERROR && newState == STATE_ERROR) { + // Keep the error icon and text around for a while longer if we keep receiving + // STATE_ERROR + mHandler.removeCallbacks(mErrorToIdleAnimationRunnable); + mHandler.postDelayed(mErrorToIdleAnimationRunnable, BiometricPrompt.HIDE_DIALOG_DELAY); + } + } + + @Override + public void onDialogAnimatedIn() { + super.onDialogAnimatedIn(); + mDialogAnimatedIn = true; + mIconController.startPulsing(); + } + + @Override + protected int getDelayAfterAuthenticatedDurationMs() { + return HIDE_DIALOG_DELAY; + } + + @Override + protected boolean shouldGrayAreaDismissDialog() { + return true; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java index 789bc926f75..7b6d3de1804 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java @@ -273,7 +273,7 @@ default void onRotationProposal(int rotation, boolean isValid) { } default void showBiometricDialog(Bundle bundle, IBiometricServiceReceiverInternal receiver, int type, boolean requireConfirmation, int userId) { } - default void onBiometricAuthenticated(boolean authenticated, String failureReason) { } + default void onBiometricAuthenticated(boolean authenticated, String failureReason, boolean requireConfirmation) { } default void onBiometricHelp(String message) { } default void onBiometricError(String error) { } default void hideBiometricDialog() { } @@ -755,11 +755,12 @@ public void showBiometricDialog(Bundle bundle, IBiometricServiceReceiverInternal } @Override - public void onBiometricAuthenticated(boolean authenticated, String failureReason) { + public void onBiometricAuthenticated(boolean authenticated, String failureReason, boolean requireConfirmation) { synchronized (mLock) { SomeArgs args = SomeArgs.obtain(); args.arg1 = authenticated; args.arg2 = failureReason; + args.arg3 = requireConfirmation; mHandler.obtainMessage(MSG_BIOMETRIC_AUTHENTICATED, args).sendToTarget(); } } @@ -1046,7 +1047,8 @@ public void handleMessage(Message msg) { for (int i = 0; i < mCallbacks.size(); i++) { mCallbacks.get(i).onBiometricAuthenticated( (boolean) someArgs.arg1 /* authenticated */, - (String) someArgs.arg2 /* failureReason */); + (String) someArgs.arg2 /* failureReason */, + (boolean) someArgs.arg3 /* requireConfirmation */); } someArgs.recycle(); break; diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java index af2f24f959b..98f359b3645 100644 --- a/services/core/java/com/android/server/biometrics/BiometricService.java +++ b/services/core/java/com/android/server/biometrics/BiometricService.java @@ -969,23 +969,23 @@ private Pair checkAndGetBiometricModality(int userId) { int firstHwAvailable = TYPE_NONE; for (int i = 0; i < mAuthenticators.size(); i++) { - modality = mAuthenticators.get(i).getType(); + int type = mAuthenticators.get(i).getType(); BiometricAuthenticator authenticator = mAuthenticators.get(i).getAuthenticator(); if (authenticator.isHardwareDetected()) { isHardwareDetected = true; if (firstHwAvailable == TYPE_NONE) { // Store the first one since we want to return the error in correct priority // order. - firstHwAvailable = modality; + firstHwAvailable = type; } if (authenticator.hasEnrolledTemplates(userId)) { hasTemplatesEnrolled = true; - if (isEnabledForApp(modality, userId)) { + if (isEnabledForApp(type, userId)) { // TODO(b/110907543): When face settings (and other settings) have both a // user toggle as well as a work profile settings page, this needs to be // updated to reflect the correct setting. enabledForApps = true; - break; + modality |= type; } } } @@ -1132,14 +1132,14 @@ private int statsModality() { if (mCurrentAuthSession == null) { return BiometricsProtoEnums.MODALITY_UNKNOWN; } - if ((mCurrentAuthSession.mModality & BiometricAuthenticator.TYPE_FINGERPRINT) + if ((mCurrentAuthSession.mModality & TYPE_FINGERPRINT) != 0) { modality |= BiometricsProtoEnums.MODALITY_FINGERPRINT; } - if ((mCurrentAuthSession.mModality & BiometricAuthenticator.TYPE_IRIS) != 0) { + if ((mCurrentAuthSession.mModality & TYPE_IRIS) != 0) { modality |= BiometricsProtoEnums.MODALITY_IRIS; } - if ((mCurrentAuthSession.mModality & BiometricAuthenticator.TYPE_FACE) != 0) { + if ((mCurrentAuthSession.mModality & TYPE_FACE) != 0) { modality |= BiometricsProtoEnums.MODALITY_FACE; } return modality; @@ -1185,6 +1185,7 @@ private void handleAuthenticationSucceeded(boolean requireConfirmation, byte[] t mCurrentAuthSession.mClientReceiver.onAuthenticationSucceeded(); mCurrentAuthSession.mState = STATE_AUTH_IDLE; mCurrentAuthSession = null; + cancelInternal(null, null, false); } else { mCurrentAuthSession.mAuthenticatedTimeMs = System.currentTimeMillis(); // Store the auth token and submit it to keystore after the confirmation @@ -1195,7 +1196,7 @@ private void handleAuthenticationSucceeded(boolean requireConfirmation, byte[] t // Notify SysUI that the biometric has been authenticated. SysUI already knows // the implicit/explicit state and will react accordingly. - mStatusBarService.onBiometricAuthenticated(true, null /* failureReason */); + mStatusBarService.onBiometricAuthenticated(true, null /* failureReason */, requireConfirmation); } catch (RemoteException e) { Slog.e(TAG, "Remote exception", e); } @@ -1210,7 +1211,7 @@ private void handleAuthenticationFailed(String failureReason) { return; } - mStatusBarService.onBiometricAuthenticated(false, failureReason); + mStatusBarService.onBiometricAuthenticated(false, failureReason, false); // TODO: This logic will need to be updated if BP is multi-modal if ((mCurrentAuthSession.mModality & TYPE_FACE) != 0) { @@ -1395,6 +1396,9 @@ private void handleOnDismissed(int reason) { // to the application KeyStore.getInstance().addAuthToken(mCurrentAuthSession.mTokenEscrow); mCurrentAuthSession.mClientReceiver.onAuthenticationSucceeded(); + // If we are using multiple modalities, we cancel the other modality that still + // might be listening for authentication + cancelInternal(null /* token */, null /* package */, false /* fromClient */); } // Do not clean up yet if we are from ConfirmDeviceCredential. We should be in the @@ -1426,6 +1430,7 @@ private void handleOnTryAgainPressed() { mCurrentAuthSession.mCallingUserId, mCurrentAuthSession.mModality, mCurrentAuthSession.mConfirmDeviceCredentialCallback); + mCurrentAuthSession = null; } private void handleOnReadyForAuthentication(int cookie, boolean requireConfirmation, @@ -1458,14 +1463,14 @@ private void handleOnReadyForAuthentication(int cookie, boolean requireConfirmat it = mCurrentAuthSession.mModalitiesMatched.entrySet().iterator(); while (it.hasNext()) { Map.Entry pair = (Map.Entry) it.next(); - if (pair.getKey() == TYPE_FINGERPRINT) { + if ((pair.getKey() & TYPE_FINGERPRINT) != 0) { mFingerprintService.startPreparedClient(pair.getValue()); - } else if (pair.getKey() == TYPE_IRIS) { + } + if ((pair.getKey() & TYPE_IRIS) != 0) { Slog.e(TAG, "Iris unsupported"); - } else if (pair.getKey() == TYPE_FACE) { + } + if ((pair.getKey() & TYPE_FACE) != 0) { mFaceService.startPreparedClient(pair.getValue()); - } else { - Slog.e(TAG, "Unknown modality: " + pair.getKey()); } modality |= pair.getKey(); } @@ -1555,13 +1560,9 @@ private void authenticateInternal(IBinder token, long sessionId, int userId, Slog.d(TAG, "Creating auth session. Modality: " + modality + ", cookie: " + cookie); final HashMap authenticators = new HashMap<>(); - authenticators.put(modality, cookie); - mPendingAuthSession = new AuthSession(authenticators, token, sessionId, userId, - receiver, opPackageName, bundle, callingUid, callingPid, callingUserId, - modality, requireConfirmation, callback); - mPendingAuthSession.mState = STATE_AUTH_CALLED; // No polymorphism :( if ((modality & TYPE_FINGERPRINT) != 0) { + authenticators.put(TYPE_FINGERPRINT, cookie); mFingerprintService.prepareForAuthentication(token, sessionId, userId, mInternalReceiver, opPackageName, cookie, callingUid, callingPid, callingUserId); @@ -1570,10 +1571,15 @@ private void authenticateInternal(IBinder token, long sessionId, int userId, Slog.w(TAG, "Iris unsupported"); } if ((modality & TYPE_FACE) != 0) { + authenticators.put(TYPE_FACE, cookie); mFaceService.prepareForAuthentication(requireConfirmation, token, sessionId, userId, mInternalReceiver, opPackageName, cookie, callingUid, callingPid, callingUserId); } + mPendingAuthSession = new AuthSession(authenticators, token, sessionId, userId, + receiver, opPackageName, bundle, callingUid, callingPid, callingUserId, + modality, requireConfirmation, callback); + mPendingAuthSession.mState = STATE_AUTH_CALLED; } catch (RemoteException e) { Slog.e(TAG, "Unable to start authentication", e); } @@ -1626,6 +1632,11 @@ private void handleCancelAuthentication(IBinder token, String opPackageName) { if (fromCDC) { if (DEBUG) Slog.d(TAG, "Cancelling from CDC"); cancelInternal(token, opPackageName, false /* fromClient */); + try { + mStatusBarService.hideBiometricDialog(); + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception", e); + } } else { cancelInternal(token, opPackageName, true /* fromClient */); } diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java index 3907f9561ab..cf39919f7e3 100644 --- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java +++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java @@ -622,11 +622,11 @@ public void showBiometricDialog(Bundle bundle, IBiometricServiceReceiverInternal } @Override - public void onBiometricAuthenticated(boolean authenticated, String failureReason) { + public void onBiometricAuthenticated(boolean authenticated, String failureReason, boolean requireConfirmation) { enforceBiometricDialog(); if (mBar != null) { try { - mBar.onBiometricAuthenticated(authenticated, failureReason); + mBar.onBiometricAuthenticated(authenticated, failureReason, requireConfirmation); } catch (RemoteException ex) { } } From 25d851395d644517e3c1f742e6676291e3c13957 Mon Sep 17 00:00:00 2001 From: rituj Date: Mon, 18 May 2020 12:23:21 +0530 Subject: [PATCH 11/26] SystemUI: BiometricPrompt: Adjust layout if FOD is present * Devices can override "biometric_dialog_fod_margin" if the default value(44dp) does not yield the desired outcome. Signed-off-by: rituj Change-Id: I0b68b1ee6c5a8031deb195fbdb0b23c33b0ab590 --- .../SystemUI/res/layout/biometric_dialog.xml | 2 +- .../SystemUI/res/values/reloaded_dimens.xml | 2 ++ .../biometrics/BiometricDialogView.java | 36 +++++++++++++++++++ 3 files changed, 39 insertions(+), 1 deletion(-) diff --git a/packages/SystemUI/res/layout/biometric_dialog.xml b/packages/SystemUI/res/layout/biometric_dialog.xml index c560d7e8f12..d3149f7a944 100644 --- a/packages/SystemUI/res/layout/biometric_dialog.xml +++ b/packages/SystemUI/res/layout/biometric_dialog.xml @@ -106,7 +106,7 @@ android:layout_width="@dimen/biometric_dialog_biometric_icon_size" android:layout_height="@dimen/biometric_dialog_biometric_icon_size" android:layout_gravity="center_horizontal" - android:layout_marginTop="48dp" + android:layout_marginTop="24dp" android:scaleType="fitXY" /> 24.0dip + + 44dp diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java index 4f14739f7ed..baf23026efb 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java @@ -17,9 +17,12 @@ package com.android.systemui.biometrics; import static android.view.accessibility.AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE; +import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL; import android.app.admin.DevicePolicyManager; import android.content.Context; +import android.content.pm.PackageManager; +import android.content.res.Configuration; import android.graphics.PixelFormat; import android.graphics.PorterDuff; import android.graphics.drawable.Drawable; @@ -29,7 +32,9 @@ import android.os.Handler; import android.os.IBinder; import android.os.Message; +import android.os.UserHandle; import android.os.UserManager; +import android.provider.Settings; import android.text.TextUtils; import android.util.DisplayMetrics; import android.util.Log; @@ -65,6 +70,7 @@ public abstract class BiometricDialogView extends LinearLayout { private static final String KEY_ERROR_TEXT_STRING = "key_error_text_string"; private static final String KEY_ERROR_TEXT_IS_TEMPORARY = "key_error_text_is_temporary"; private static final String KEY_ERROR_TEXT_COLOR = "key_error_text_color"; + private static final String FOD = "vendor.pa.biometrics.fingerprint.inscreen"; private static final int ANIMATION_DURATION_SHOW = 250; // ms private static final int ANIMATION_DURATION_AWAY = 350; // ms @@ -112,6 +118,7 @@ public abstract class BiometricDialogView extends LinearLayout { private boolean mIsFace; protected boolean mRequireConfirmation; private int mUserId; // used to determine if we should show work background + private final boolean mHasFod; private boolean mCompletedAnimatingIn; private boolean mPendingDismissDialog; @@ -169,6 +176,7 @@ public BiometricDialogView(Context context, DialogViewCallback callback) { .getDimension(R.dimen.biometric_dialog_animation_translation_offset); mErrorColor = getResources().getColor(R.color.biometric_dialog_error); mTextColor = getResources().getColor(R.color.biometric_dialog_gray); + mHasFod = mPackageManager.hasSystemFeature(FOD); DisplayMetrics metrics = new DisplayMetrics(); mWindowManager.getDefaultDisplay().getMetrics(metrics); @@ -355,6 +363,34 @@ protected int getAnimatingAwayDuration() { public void setFaceAndFingerprint(boolean isFace, boolean isFingerprint) { mIsFace = isFace; mIsFingerprint = isFingerprint; + if (mIsFingerprint) { + mBiometricIcon.setVisibility(mHasFod ? View.INVISIBLE : View.VISIBLE); + boolean isPortrait = (getResources().getConfiguration().orientation + == Configuration.ORIENTATION_PORTRAIT); + if (mHasFod && isPortrait) { + boolean isGesturalNav= Integer.parseInt(Settings.Secure.getStringForUser( + mContext.getContentResolver(), Settings.Secure.NAVIGATION_MODE, + UserHandle.USER_CURRENT)) == NAV_BAR_MODE_GESTURAL; + + final int navbarHeight = getResources().getDimensionPixelSize( + com.android.internal.R.dimen.navigation_bar_height); + final int fodMargin = getResources().getDimensionPixelSize( + R.dimen.biometric_dialog_fod_margin); + + LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mBiometricIcon.getLayoutParams(); + lp.topMargin = isGesturalNav ? fodMargin : (fodMargin > navbarHeight) + ? (fodMargin - navbarHeight) : 0; + + // Add Errortext above the biometric icon + mDialog.removeView(mErrorText); + mDialog.addView(mErrorText, mDialog.indexOfChild(mBiometricIcon)); + lp = (LinearLayout.LayoutParams) mDescriptionText.getLayoutParams(); + lp.bottomMargin = mErrorText.getPaddingTop(); + mErrorText.setPadding(0, 0, 0, 0); + } + } else if (mIsFace) { + mBiometricIcon.setVisibility(View.VISIBLE); + } } private void setDismissesDialog(View v) { From 2fbe83c9db9f7f5357be6cb7b5ef43335902c171 Mon Sep 17 00:00:00 2001 From: rituj Date: Tue, 19 May 2020 22:09:24 +0530 Subject: [PATCH 12/26] SystemUI: BiometricPrompt: Call onDialogAnimatedIn when we are showing the dialog without animation * This fixes a bug where the dialog gets stuck on screen rotation and mPendingDismissDialog is true Signed-off-by: rituj Change-Id: I0c6ae1a03626ed79cb8222342712be8f10f2a72c --- .../com/android/systemui/biometrics/BiometricDialogView.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java index baf23026efb..74feb8c4c73 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java @@ -345,7 +345,7 @@ public void onAttachedToWindow() { mDialog.setAlpha(1.0f); mDialog.setTranslationY(0); mLayout.setAlpha(1.0f); - mCompletedAnimatingIn = true; + onDialogAnimatedIn(); } else { // Dim the background and slide the dialog up mDialog.setTranslationY(mAnimationTranslationOffset); From e4ed0e7091a2edf8f05bad9d212ae57d85c14d37 Mon Sep 17 00:00:00 2001 From: Evan Anderson Date: Tue, 14 Jul 2020 20:51:34 -0500 Subject: [PATCH 13/26] SystemUI: BiometricDialogView: Fix missing package manager member * commit a949f3d1cd859004dc7c8608ffdf5760bfade56b uses mPackageManager but does not declare the local variable Change-Id: I29e60e881dc81a0894c6ae693ad07c8f7a366887 --- .../com/android/systemui/biometrics/BiometricDialogView.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java index 74feb8c4c73..a961a710c97 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java @@ -93,6 +93,7 @@ public abstract class BiometricDialogView extends LinearLayout { private final int mErrorColor; private final float mDialogWidth; protected final DialogViewCallback mCallback; + private final PackageManager mPackageManager; protected final ViewGroup mLayout; protected final LinearLayout mDialog; @@ -172,6 +173,7 @@ public BiometricDialogView(Context context, DialogViewCallback callback) { mWindowManager = mContext.getSystemService(WindowManager.class); mUserManager = mContext.getSystemService(UserManager.class); mDevicePolicyManager = mContext.getSystemService(DevicePolicyManager.class); + mPackageManager = mContext.getPackageManager(); mAnimationTranslationOffset = getResources() .getDimension(R.dimen.biometric_dialog_animation_translation_offset); mErrorColor = getResources().getColor(R.color.biometric_dialog_error); From df520f8377b860ef300c7ba3c3d8aefa77bc6e63 Mon Sep 17 00:00:00 2001 From: rituj Date: Sat, 25 Jul 2020 15:29:22 +0530 Subject: [PATCH 14/26] SystemUI: Hide non-system overlays when biometric prompt is showing Signed-off-by: rituj Change-Id: I46668a3f747081dc35e76258a9fff2448b3770a5 --- .../com/android/systemui/biometrics/BiometricDialogView.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java index a961a710c97..bb4f952b1b7 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java @@ -604,7 +604,8 @@ public WindowManager.LayoutParams getLayoutParams() { WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL, WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED, PixelFormat.TRANSLUCENT); - lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS; + lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS + | WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS; lp.setTitle("BiometricDialogView"); lp.token = mWindowToken; return lp; From 5acc1b7739a7d13df130d3361906bd88636f3d29 Mon Sep 17 00:00:00 2001 From: rituj Date: Thu, 30 Apr 2020 19:13:56 +0530 Subject: [PATCH 15/26] base: Introduce Applock [1/2] * Built upon the initial commit by "Anass Karbila " Signed-off-by: rituj Change-Id: Ida21d22035d708a359eaaa29e2c48ff6f0dd6627 --- Android.bp | 2 + core/java/android/app/AppLockManager.java | 111 +++ core/java/android/app/IAppLockCallback.aidl | 23 + core/java/android/app/IAppLockService.aidl | 39 + .../android/app/SystemServiceRegistry.java | 9 + core/java/android/content/Context.java | 10 + .../hardware/biometrics/BiometricPrompt.java | 15 + core/java/android/provider/Settings.java | 15 + .../SystemUI/res/values/reloaded_dimens.xml | 3 + .../SystemUI/res/values/reloaded_strings.xml | 6 + .../biometrics/BiometricDialogView.java | 76 +- .../systemui/biometrics/FaceDialogView.java | 6 +- .../NotificationEntryManager.java | 80 ++ ...NotificationInterruptionStateProvider.java | 9 + .../collection/NotificationData.java | 14 + .../row/ExpandableNotificationRow.java | 81 +- .../locksettings/LockSettingsService.java | 2 + .../com/android/server/wm/ActivityRecord.java | 22 + .../android/server/wm/ActivityStarter.java | 12 + .../wm/ActivityTaskManagerDebugConfig.java | 4 +- .../server/wm/ActivityTaskManagerService.java | 49 +- .../com/android/server/wm/AppLockService.java | 804 ++++++++++++++++++ .../com/android/server/wm/AppWindowToken.java | 2 +- .../java/com/android/server/SystemServer.java | 5 + 24 files changed, 1381 insertions(+), 18 deletions(-) create mode 100644 core/java/android/app/AppLockManager.java create mode 100644 core/java/android/app/IAppLockCallback.aidl create mode 100644 core/java/android/app/IAppLockService.aidl create mode 100644 services/core/java/com/android/server/wm/AppLockService.java diff --git a/Android.bp b/Android.bp index b83893c7328..846126a0b74 100755 --- a/Android.bp +++ b/Android.bp @@ -64,6 +64,8 @@ java_defaults { "core/java/android/app/IAlarmListener.aidl", "core/java/android/app/IAlarmManager.aidl", "core/java/android/app/IAppTask.aidl", + "core/java/android/app/IAppLockCallback.aidl", + "core/java/android/app/IAppLockService.aidl", "core/java/android/app/IApplicationThread.aidl", "core/java/android/app/IAssistDataReceiver.aidl", "core/java/android/app/ITaskStackListener.aidl", diff --git a/core/java/android/app/AppLockManager.java b/core/java/android/app/AppLockManager.java new file mode 100644 index 00000000000..c980e091af5 --- /dev/null +++ b/core/java/android/app/AppLockManager.java @@ -0,0 +1,111 @@ +/** + * Copyright (C) 2017-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 android.app; + +import android.annotation.SystemService; +import android.content.Context; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.ServiceManager.ServiceNotFoundException; + +import java.util.List; + +/** + * @author Anas Karbila + * @author Rituj Beniwal + * @hide + */ +@SystemService(Context.APPLOCK_SERVICE) +public class AppLockManager { + + private static final String TAG = "AppLockManager"; + + private IAppLockService mService; + + public AppLockManager(IAppLockService service) { + mService = service; + } + + public void addAppToList(String packageName) { + try { + mService.addAppToList(packageName); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + public void removeAppFromList(String packageName) { + try { + mService.removeAppFromList(packageName); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + public boolean isAppLocked(String packageName) { + try { + return mService.isAppLocked(packageName); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + public boolean isAppOpen(String packageName) { + try { + return mService.isAppOpen(packageName); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + public int getLockedAppsCount() { + try { + return mService.getLockedAppsCount(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + public List getLockedPackages() { + try { + return mService.getLockedPackages(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + public void addAppLockCallback(IAppLockCallback c) { + try { + mService.addAppLockCallback(c); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + public void removeAppLockCallback(IAppLockCallback c) { + try { + mService.removeAppLockCallback(c); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + public abstract static class AppLockCallback extends IAppLockCallback.Stub { + @Override + public abstract void onAppStateChanged(String pkg, boolean opened); + }; +} diff --git a/core/java/android/app/IAppLockCallback.aidl b/core/java/android/app/IAppLockCallback.aidl new file mode 100644 index 00000000000..1719613b8e3 --- /dev/null +++ b/core/java/android/app/IAppLockCallback.aidl @@ -0,0 +1,23 @@ +/** + * Copyright (C) 2017-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 android.app; + +/** @hide */ +oneway interface IAppLockCallback { + + void onAppStateChanged(String packageName, boolean opened); +} \ No newline at end of file diff --git a/core/java/android/app/IAppLockService.aidl b/core/java/android/app/IAppLockService.aidl new file mode 100644 index 00000000000..30c8c4010ea --- /dev/null +++ b/core/java/android/app/IAppLockService.aidl @@ -0,0 +1,39 @@ +/** + * Copyright (C) 2017-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 android.app; + +import android.app.IAppLockCallback; + +/** @hide */ +interface IAppLockService { + + void addAppToList(in String packageName); + + void removeAppFromList(in String packageName); + + boolean isAppLocked(in String packageName); + + boolean isAppOpen(in String packageName); + + int getLockedAppsCount(); + + List getLockedPackages(); + + void addAppLockCallback(IAppLockCallback callback); + + void removeAppLockCallback(IAppLockCallback callback); +} diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index 95254cdccba..0189f02e032 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -959,6 +959,15 @@ public BiometricManager createService(ContextImpl ctx) } }); + registerService(Context.APPLOCK_SERVICE, AppLockManager.class, + new CachedServiceFetcher() { + @Override + public AppLockManager createService(ContextImpl ctx) throws ServiceNotFoundException { + IBinder b = ServiceManager.getServiceOrThrow(Context.APPLOCK_SERVICE); + IAppLockService service = IAppLockService.Stub.asInterface(b); + return new AppLockManager(service); + }}); + registerService(Context.TV_INPUT_SERVICE, TvInputManager.class, new CachedServiceFetcher() { @Override diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 7056283c697..2f186b4420c 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -4691,6 +4691,16 @@ public abstract boolean startInstrumentation(@NonNull ComponentName className, */ public static final String LONGSCREENSHOT_SERVICE = "longshot"; + /** + * Use with {@link #getSystemService} to retrieve a + * {@link android.app.AppLockManager} for accessing and setting locked apps state. + * + * @hide + * @see #getSystemService + * @see android.app.AppLockManager + */ + public static final String APPLOCK_SERVICE = "applock"; + /** * Determine whether the given permission is allowed for a particular * process and user ID running in the system. diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java index 1142a07bc66..8b3748cb2ed 100644 --- a/core/java/android/hardware/biometrics/BiometricPrompt.java +++ b/core/java/android/hardware/biometrics/BiometricPrompt.java @@ -58,6 +58,10 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan * @hide */ public static final String KEY_USE_DEFAULT_TITLE = "use_default_title"; + /** + * @hide + */ + public static final String KEY_APPLOCK_PKG = "applock_package_name"; /** * @hide */ @@ -160,6 +164,17 @@ public Builder(Context context) { return this; } + /** + * Optional: Show a special dialog for app locker if KEY_APPLOCK_PKG is set + * @param packageName + * @return + * @hide + */ + @NonNull public Builder setApplockPackage(@NonNull CharSequence packageName) { + mBundle.putCharSequence(KEY_APPLOCK_PKG, packageName); + return this; + } + /** * Optional: Set the subtitle to display. * @param subtitle diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 96ed81f9eb2..59b109adae8 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -3570,6 +3570,21 @@ public boolean validate(@Nullable String value) { */ public static final int SCREEN_BRIGHTNESS_MODE_AUTOMATIC = 1; + /** + * Indicates whether we should only show the app lock view when the device is woken up + * Or always. + * @hide + */ + public static final String APP_LOCK_SHOW_ONLY_ON_WAKE = "app_lock_show_only_on_wake"; + + + /** + * Indicates whether we should only show the app lock view when the device is woken up + * Or always. + * @hide + */ + public static final String APP_LOCK_HIDE_NOTIFICATIONS = "app_lock_hide_notifications"; + /** * Control whether to enable adaptive sleep mode. * @hide diff --git a/packages/SystemUI/res/values/reloaded_dimens.xml b/packages/SystemUI/res/values/reloaded_dimens.xml index e41303696dd..f7d07f11f12 100644 --- a/packages/SystemUI/res/values/reloaded_dimens.xml +++ b/packages/SystemUI/res/values/reloaded_dimens.xml @@ -20,4 +20,7 @@ 44dp + + + 54dp diff --git a/packages/SystemUI/res/values/reloaded_strings.xml b/packages/SystemUI/res/values/reloaded_strings.xml index 55bdf295fad..1aa09e0e364 100644 --- a/packages/SystemUI/res/values/reloaded_strings.xml +++ b/packages/SystemUI/res/values/reloaded_strings.xml @@ -132,4 +132,10 @@ Reboot Recovery + +  is Locked. + , fingerprint or your face to unlock. +  or your face to unlock. +  or your fingerprint to unlock. + App is locked diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java index bb4f952b1b7..2870fea51ea 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java @@ -17,10 +17,13 @@ package com.android.systemui.biometrics; import static android.view.accessibility.AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE; +import static android.view.Gravity.CENTER; +import static android.view.Gravity.START; import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL; import android.app.admin.DevicePolicyManager; import android.content.Context; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.res.Configuration; import android.graphics.PixelFormat; @@ -101,6 +104,7 @@ public abstract class BiometricDialogView extends LinearLayout { protected final TextView mSubtitleText; protected final TextView mDescriptionText; protected final ImageView mBiometricIcon; + protected final ImageView mAppIcon; protected final TextView mErrorText; protected final Button mPositiveButton; protected final Button mNegativeButton; @@ -115,6 +119,7 @@ public abstract class BiometricDialogView extends LinearLayout { private boolean mAnimatingAway; private boolean mWasForceRemoved; private boolean mSkipIntro; + protected boolean mAppLockDialog; private boolean mIsFingerprint; private boolean mIsFace; protected boolean mRequireConfirmation; @@ -145,7 +150,6 @@ public void run() { .translationY(0) .setDuration(ANIMATION_DURATION_SHOW) .setInterpolator(mLinearOutSlowIn) - .withLayer() .withEndAction(() -> onDialogAnimatedIn()) .start(); } @@ -222,6 +226,25 @@ public boolean onKey(View v, int keyCode, KeyEvent event) { mPositiveButton = mLayout.findViewById(R.id.button1); mTryAgainButton = mLayout.findViewById(R.id.button_try_again); + mAppIcon = new ImageView(context); + final int iconDim = getResources().getDimensionPixelSize( + R.dimen.applock_icon_dimension); + LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(iconDim, iconDim); + lp.gravity = CENTER; + lp.topMargin = -iconDim/2; + lp.bottomMargin = iconDim/4; + mAppIcon.setLayoutParams(lp); + mAppIcon.setVisibility(View.GONE); + mDialog.addView(mAppIcon, 0); + ((ViewGroup) mDialog.getParent()).setClipChildren(false); + ((ViewGroup) mDialog.getParent().getParent()).setClipChildren(false); + ((ViewGroup) mDialog.getParent().getParent().getParent()).setClipChildren(false); + + lp = (LinearLayout.LayoutParams) mDialog.getLayoutParams(); + lp.leftMargin = 0; + lp.bottomMargin = 0; + lp.rightMargin = 0; + mBiometricIcon.setContentDescription( getResources().getString(getIconDescriptionResourceId())); @@ -311,10 +334,33 @@ public void onAttachedToWindow() { updateState(mState); } - CharSequence titleText = mBundle.getCharSequence(BiometricPrompt.KEY_TITLE); - - mTitleText.setVisibility(View.VISIBLE); - mTitleText.setText(titleText); + final CharSequence applockPackage = mBundle.getCharSequence(BiometricPrompt.KEY_APPLOCK_PKG); + final CharSequence titleText = mBundle.getCharSequence(BiometricPrompt.KEY_TITLE); + if (TextUtils.isEmpty(applockPackage)) { + mAppLockDialog = false; + mTitleText.setVisibility(View.VISIBLE); + mTitleText.setText(titleText); + mAppIcon.setVisibility(View.GONE); + mDescriptionText.setGravity(START); + } else { + mAppLockDialog = true; + ApplicationInfo aInfo = null; + try { + aInfo = mPackageManager.getApplicationInfoAsUser(applockPackage.toString(), 0, mUserId); + } catch(PackageManager.NameNotFoundException e) { + } + Drawable icon = (aInfo == null) ? null : mPackageManager.getApplicationIcon(aInfo); + if (icon == null) { + mTitleText.setVisibility(View.VISIBLE); + mTitleText.setText("Unlock " + titleText.toString()); + mAppIcon.setVisibility(View.GONE); + } else { + mTitleText.setVisibility(View.GONE); + mAppIcon.setVisibility(View.VISIBLE); + mAppIcon.setImageDrawable(icon); + } + mDescriptionText.setGravity(CENTER); + } final CharSequence subtitleText = mBundle.getCharSequence(BiometricPrompt.KEY_SUBTITLE); if (TextUtils.isEmpty(subtitleText)) { @@ -325,13 +371,18 @@ public void onAttachedToWindow() { mSubtitleText.setText(subtitleText); } - final CharSequence descriptionText = + CharSequence descriptionText = mBundle.getCharSequence(BiometricPrompt.KEY_DESCRIPTION); if (TextUtils.isEmpty(descriptionText)) { mDescriptionText.setVisibility(View.GONE); announceAccessibilityEvent(); } else { mDescriptionText.setVisibility(View.VISIBLE); + if (mAppLockDialog) { + final CharSequence negText = mBundle.getCharSequence(BiometricPrompt.KEY_NEGATIVE_TEXT); + descriptionText += getResources().getString(R.string.applock_locked) + "\n" + + negText + getResources().getString(getDescriptionTextId()); + } mDescriptionText.setText(descriptionText); } @@ -358,8 +409,18 @@ public void onAttachedToWindow() { mSkipIntro = false; } + private int getDescriptionTextId() { + if (mIsFingerprint && mIsFace) { + return R.string.applock_fingerprint_face; + } else if (mIsFace) { + return R.string.applock_face; + } else { + return R.string.applock_fingerprint; + } + } + protected int getAnimatingAwayDuration() { - return ANIMATION_DURATION_AWAY; + return (int) ((mAppLockDialog ? 1.3f : 1f ) * (float) ANIMATION_DURATION_AWAY); } public void setFaceAndFingerprint(boolean isFace, boolean isFingerprint) { @@ -439,7 +500,6 @@ public void run() { .translationY(mAnimationTranslationOffset) .setDuration(getAnimatingAwayDuration()) .setInterpolator(mLinearOutSlowIn) - .withLayer() .withEndAction(endActionRunnable) .start(); } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/FaceDialogView.java b/packages/SystemUI/src/com/android/systemui/biometrics/FaceDialogView.java index ae6cb5ce23d..73725a640c7 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/FaceDialogView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/FaceDialogView.java @@ -165,7 +165,7 @@ private void updateSize(int newSize) { if (newSize == SIZE_SMALL) { // These fields are required and/or always hold a spot on the UI, so should be set to // INVISIBLE so they keep their position - mTitleText.setVisibility(View.INVISIBLE); + mTitleText.setVisibility(mAppLockDialog ? View.GONE : View.INVISIBLE); mErrorText.setVisibility(View.INVISIBLE); mNegativeButton.setVisibility(View.INVISIBLE); @@ -247,7 +247,7 @@ private void updateSize(int newSize) { public void onAnimationStart(Animator animation) { super.onAnimationStart(animation); // Set the visibility of opacity-animating views back to VISIBLE - mTitleText.setVisibility(View.VISIBLE); + if (!mAppLockDialog) mTitleText.setVisibility(View.VISIBLE); mErrorText.setVisibility(View.VISIBLE); mNegativeButton.setVisibility(View.VISIBLE); mTryAgainButton.setVisibility(View.VISIBLE); @@ -264,6 +264,7 @@ public void onAnimationStart(Animator animation) { public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); mSize = SIZE_BIG; + updateSize(mSize); } }); as.play(outlineAnimator).with(iconAnimator).with(opacityAnimator) @@ -286,7 +287,6 @@ public void onSaveState(Bundle bundle) { bundle.putBoolean(KEY_DIALOG_ANIMATED_IN, mDialogAnimatedIn); } - @Override protected void handleResetMessage() { mErrorText.setTextColor(mTextColor); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java index da0f83da5a7..c2893a232df 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java @@ -19,8 +19,15 @@ import static android.service.notification.NotificationListenerService.REASON_ERROR; import android.annotation.Nullable; +import android.app.AppLockManager; +import android.app.AppLockManager.AppLockCallback; import android.app.Notification; +import android.content.ContentResolver; import android.content.Context; +import android.database.ContentObserver; +import android.os.Bundle; +import android.os.UserHandle; +import android.provider.Settings; import android.service.notification.NotificationListenerService; import android.service.notification.StatusBarNotification; import android.util.ArrayMap; @@ -30,6 +37,7 @@ import com.android.internal.statusbar.NotificationVisibility; import com.android.systemui.Dependency; import com.android.systemui.Dumpable; +import com.android.systemui.R; import com.android.systemui.statusbar.NotificationLifetimeExtender; import com.android.systemui.statusbar.NotificationPresenter; import com.android.systemui.statusbar.NotificationRemoteInputManager; @@ -41,6 +49,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationRowBinder; import com.android.systemui.statusbar.notification.logging.NotificationLogger; +import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.NotificationContentInflater; import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; @@ -98,6 +107,30 @@ public class NotificationEntryManager implements private final List mNotificationEntryListeners = new ArrayList<>(); private NotificationRemoveInterceptor mRemoveInterceptor; + private SettingsObserver mSettingsObserver; + private boolean mLockNotifications; + private final AppLockManager mAppLockManager; + private final AppLockCallback mAppLockCallback = new AppLockCallback() { + @Override + public void onAppStateChanged(String pkg, boolean open) { + updateAppNotifications(pkg, open); + } + }; + + private void updateAppNotifications(String pkg, boolean open) { + ArrayList arr = mNotificationData.getAllNotificationsForPackage(pkg); + for (NotificationEntry notif : arr) { + if (notif.rowExists()) { + ExpandableNotificationRow row = notif.getRow(); + boolean appLocked = mAppLockManager.isAppLocked(pkg); + row.setAppLocked(appLocked); + Dependency.get(Dependency.MAIN_HANDLER).post(() -> { + row.onAppStateChanged(!mLockNotifications || open); + }); + } + } + } + @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { pw.println("NotificationEntryManager state:"); @@ -124,6 +157,10 @@ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { @Inject public NotificationEntryManager(Context context) { mNotificationData = new NotificationData(); + mAppLockManager = (AppLockManager) context.getSystemService(Context.APPLOCK_SERVICE); + mAppLockManager.addAppLockCallback(mAppLockCallback); + mSettingsObserver = new SettingsObserver(context); + mSettingsObserver.observe(); } /** Adds a {@link NotificationEntryListener}. */ @@ -236,6 +273,16 @@ public void onAsyncInflationFinished(NotificationEntry entry, // If there was an async task started after the removal, we don't want to add it back to // the list, otherwise we might get leaks. if (!entry.isRowRemoved()) { + final String pkg = entry.notification.getPackageName(); + boolean isAppLocked = mAppLockManager.isAppLocked(pkg); + if (isAppLocked && entry.rowExists()) { + ExpandableNotificationRow row = entry.getRow(); + row.setAppLocked(isAppLocked); + Dependency.get(Dependency.MAIN_HANDLER).post(() -> { + row.onAppStateChanged(!mLockNotifications || + mAppLockManager.isAppOpen(pkg)); + }); + } boolean isNew = mNotificationData.get(entry.key) == null; if (isNew) { for (NotificationEntryListener listener : mNotificationEntryListeners) { @@ -549,4 +596,37 @@ private NotificationRowBinder requireBinder() { } return mNotificationRowBinder; } + + private class SettingsObserver extends ContentObserver { + + private ContentResolver mCr; + + SettingsObserver(Context context) { + super(Dependency.get(Dependency.MAIN_HANDLER)); + mCr = context.getContentResolver(); + } + + void observe() { + mCr.registerContentObserver(Settings.System.getUriFor( + Settings.System.APP_LOCK_HIDE_NOTIFICATIONS), false, this, + UserHandle.USER_ALL); + mLockNotifications = Settings.System.getIntForUser(mCr, + Settings.System.APP_LOCK_HIDE_NOTIFICATIONS, 1, + UserHandle.USER_CURRENT) != 0; + } + + @Override + public void onChange(boolean selfChange) { + boolean lockNotifications = Settings.System.getIntForUser(mCr, + Settings.System.APP_LOCK_HIDE_NOTIFICATIONS, 1, + UserHandle.USER_CURRENT) != 0; + if (mLockNotifications != lockNotifications) { + mLockNotifications = lockNotifications; + List lockedPackages = mAppLockManager.getLockedPackages(); + for (String pkg : lockedPackages) { + updateAppNotifications(pkg, mAppLockManager.isAppOpen(pkg)); + } + } + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java index ef09434aa39..1c249bd0ba0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java @@ -164,6 +164,10 @@ public void setUpWithPresenter( * @return true if the entry should bubble up, false otherwise */ public boolean shouldBubbleUp(NotificationEntry entry) { + if (entry.rowExists() && entry.getRow().isAppLocked()) { + return false; + } + final StatusBarNotification sbn = entry.notification; if (!canAlertCommon(entry)) { @@ -216,6 +220,11 @@ public boolean shouldHeadsUp(NotificationEntry entry) { } private boolean shouldHeadsUpWhenAwake(NotificationEntry entry) { + if (mStatusBarStateController.getState() != StatusBarState.KEYGUARD + && entry.rowExists() && entry.getRow().blockHeadsUp()) { + return false; + } + StatusBarNotification sbn = entry.notification; if (!mUseHeadsUp) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java index 7b4ed3f8a7d..f41ad08e8f4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java @@ -185,6 +185,20 @@ public NotificationEntry get(String key) { return mEntries.get(key); } + public ArrayList getAllNotificationsForPackage(String pkg) { + synchronized (mEntries) { + final int len = mEntries.size(); + ArrayList filtered = new ArrayList<>(len); + for (int i = 0; i < len; i++) { + NotificationEntry entry = mEntries.valueAt(i); + if (pkg.equals(entry.notification.getPackageName())) { + filtered.add(entry); + } + } + return filtered; + } + } + public void add(NotificationEntry entry) { synchronized (mEntries) { mEntries.put(entry.notification.getKey(), entry); 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 12d537d3c64..6ec3a60b436 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 @@ -23,6 +23,7 @@ import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_CONTRACTED; import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_HEADSUP; +import android.app.AppLockManager; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; @@ -30,6 +31,8 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.app.NotificationChannel; +import android.app.PendingIntent; +import android.content.ComponentName; import android.content.Context; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; @@ -44,6 +47,8 @@ import android.os.Build; import android.os.Bundle; import android.os.SystemClock; +import android.os.UserHandle; +import android.provider.Settings; import android.service.notification.StatusBarNotification; import android.util.ArraySet; import android.util.AttributeSet; @@ -327,6 +332,10 @@ public Float get(ExpandableNotificationRow object) { private SystemNotificationAsyncTask mSystemNotificationAsyncTask = new SystemNotificationAsyncTask(); + private boolean mAppOpen; + private boolean mIsAppLocked; + private boolean mIsAlarmOrCall; + /** * 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 @@ -445,6 +454,22 @@ private void setIconRunning(ImageView imageView, boolean running) { public void setEntry(@NonNull NotificationEntry entry) { mEntry = entry; mStatusBarNotification = entry.notification; + if (mStatusBarNotification != null) { + updateAlarmOrCall(); + AppLockManager appLockManager = (AppLockManager) mContext + .getSystemService(Context.APPLOCK_SERVICE); + String pkg = mStatusBarNotification.getPackageName(); + mIsAppLocked = appLockManager.isAppLocked(pkg); + if (mIsAppLocked) { + boolean lockNotifications = Settings.System.getIntForUser( + mContext.getContentResolver(), + Settings.System.APP_LOCK_HIDE_NOTIFICATIONS, 1, + UserHandle.USER_CURRENT) != 0; + mAppOpen = appLockManager.isAppOpen(pkg) || !lockNotifications; + } + if (mIsAppLocked) Log.d(TAG, "setEntry() app:" + mAppName + + " mAppOpen:" + mAppOpen + " mIsAlarmOrCall:" + mIsAlarmOrCall); + } cacheIsSystemNotification(); } @@ -1615,6 +1640,7 @@ public void setInflationCallback(InflationCallback callback) { } public void setNeedsRedaction(boolean needsRedaction) { + needsRedaction |= mIsAppLocked && !mAppOpen; if (mNeedsRedaction != needsRedaction) { mNeedsRedaction = needsRedaction; updateInflationFlag(FLAG_CONTENT_VIEW_PUBLIC, needsRedaction /* shouldInflate */); @@ -2318,7 +2344,7 @@ public int getIntrinsicHeight() { return mGuts.getIntrinsicHeight(); } else if ((isChildInGroup() && !isGroupExpanded())) { return mPrivateLayout.getMinHeight(); - } else if (mSensitive && mHideSensitiveForIntrinsicHeight) { + } else if (shouldShowPublic()) { return getMinHeight(); } else if (mIsSummaryWithChildren) { return mChildrenContainer.getIntrinsicHeight(); @@ -2342,7 +2368,7 @@ public int getIntrinsicHeight() { * except for legacy use cases. */ public boolean canShowHeadsUp() { - if (mOnKeyguard && !isDozing() && !isBypassEnabled()) { + if (mOnKeyguard && !isDozing() && !isBypassEnabled() || (mIsAppLocked && !mAppOpen)) { return false; } return true; @@ -2513,7 +2539,8 @@ public void setHideSensitive(boolean hideSensitive, boolean animated, long delay return; } boolean oldShowingPublic = mShowingPublic; - mShowingPublic = mSensitive && hideSensitive; + mShowingPublic = (mSensitive && hideSensitive) || (mIsAppLocked && !mAppOpen + && !mIsAlarmOrCall); if (mShowingPublicInitialized && mShowingPublic == oldShowingPublic) { return; } @@ -2574,6 +2601,51 @@ public void run() { } } + public void onAppStateChanged(boolean open) { + if (mAppOpen != open) { + mAppOpen = open; + setNeedsRedaction(mNeedsRedaction); + setHideSensitive(mSensitive, true, 0, 100); + } + } + + public void setAppLocked(boolean locked) { + mIsAppLocked = locked; + } + + public boolean isAppLocked() { + return mIsAppLocked; + } + + public boolean blockHeadsUp() { + if (mIsAppLocked) Log.d(TAG, "blockHeadsUp() app:" + mAppName + + " mAppOpen:" + mAppOpen + " mIsAlarmOrCall:" + mIsAlarmOrCall); + return mIsAppLocked && !mAppOpen && !mIsAlarmOrCall; + } + + private void updateAlarmOrCall() { + PendingIntent intent = mStatusBarNotification.getNotification().contentIntent; + if (intent == null) { + mIsAlarmOrCall = false; + return; + } + + ComponentName cmp = intent.getIntent().getComponent(); + if (cmp != null) { + String intentClassName = cmp.getClassName().toLowerCase(); + mIsAlarmOrCall = (intentClassName.contains("call") || intentClassName.contains("voip") + || intentClassName.contains("alarm")) + && intentClassName.contains("activity"); + } else { + intent = mStatusBarNotification.getNotification().fullScreenIntent; + if (intent != null) { + mIsAlarmOrCall = true; + } else { + mIsAlarmOrCall = false; + } + } + } + @Override public boolean mustStayOnScreen() { return mIsHeadsUp && mMustStayOnScreen; @@ -2589,7 +2661,8 @@ public boolean canViewBeDismissed() { } private boolean shouldShowPublic() { - return mSensitive && mHideSensitiveForIntrinsicHeight; + return (mSensitive && mHideSensitiveForIntrinsicHeight) || (mIsAppLocked && !mAppOpen + && !mIsAlarmOrCall); } public void makeActionsVisibile() { diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java index 81e1b53a66d..782c94d9b40 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsService.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java @@ -120,6 +120,7 @@ import com.android.server.locksettings.SyntheticPasswordManager.AuthenticationResult; import com.android.server.locksettings.SyntheticPasswordManager.AuthenticationToken; import com.android.server.locksettings.recoverablekeystore.RecoverableKeyStoreManager; +import com.android.server.wm.AppLockService; import com.android.server.wm.WindowManagerInternal; import libcore.util.HexEncoding; @@ -2119,6 +2120,7 @@ private void notifyPasswordChanged(@UserIdInt int userId) { mContext.getSystemService(Context.DEVICE_POLICY_SERVICE); dpm.reportPasswordChanged(userId); LocalServices.getService(WindowManagerInternal.class).reportPasswordChanged(userId); + LocalServices.getService(AppLockService.class).reportPasswordChanged(userId); }); } diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 9656b181379..bfc1b67569e 100755 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -115,6 +115,7 @@ import static com.android.server.wm.ActivityStack.PAUSE_TIMEOUT_MSG; import static com.android.server.wm.ActivityStack.STACK_VISIBILITY_VISIBLE; import static com.android.server.wm.ActivityStack.STOP_TIMEOUT_MSG; +import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_APPLOCK; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CONFIGURATION; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_FOCUS; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SAVED_STATE; @@ -122,6 +123,7 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_TRANSITION; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_VISIBILITY; +import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_APPLOCK; import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_CONFIGURATION; import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_FOCUS; import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_SAVED_STATE; @@ -245,6 +247,7 @@ public final class ActivityRecord extends ConfigurationContainer { private static final String TAG_SWITCH = TAG + POSTFIX_SWITCH; private static final String TAG_VISIBILITY = TAG + POSTFIX_VISIBILITY; private static final String TAG_FOCUS = TAG + POSTFIX_FOCUS; + private static final String TAG_APPLOCK = TAG + POSTFIX_APPLOCK; // TODO(b/67864419): Remove once recents component is overridden private static final String LEGACY_RECENTS_PACKAGE_NAME = "com.android.systemui.recents"; @@ -280,6 +283,7 @@ public final class ActivityRecord extends ConfigurationContainer { final String processName; // process where this component wants to run final String taskAffinity; // as per ActivityInfo.taskAffinity final boolean stateNotNeeded; // As per ActivityInfo.flags + boolean isAppLocked; boolean fullscreen; // The activity is opaque and fills the entire space of this task. // TODO: See if it possible to combine this with the fullscreen field. final boolean hasWallpaper; // Has a wallpaper window as a background. @@ -298,6 +302,7 @@ public final class ActivityRecord extends ConfigurationContainer { private int realTheme; // actual theme resource we will use, never 0. private int windowFlags; // custom window flags for preview window. public int perfActivityBoostHandler = -1; //perflock handler when activity is created. + private int mResizeMode = -1; private TaskRecord task; // the task this is in. private long createTime = System.currentTimeMillis(); long lastVisibleTime; // last time this activity became visible @@ -1033,6 +1038,7 @@ boolean isResolverOrChildActivity() { packageName = aInfo.applicationInfo.packageName; launchMode = aInfo.launchMode; + isAppLocked = mAtmService.isAppLocked(packageName); Entry ent = AttributeCache.instance().get(packageName, realTheme, com.android.internal.R.styleable.Window, mUserId); @@ -1276,6 +1282,16 @@ boolean canLaunchHomeActivity(int uid, ActivityRecord sourceRecord) { return sourceRecord != null && sourceRecord.isResolverOrDelegateActivity(); } + boolean getIsAppLocked() { + isAppLocked = mAtmService.isAppLocked(packageName); + if (isAppLocked) { + info.resizeMode = RESIZE_MODE_UNRESIZEABLE; + } else { + info.resizeMode = mResizeMode; + } + return isAppLocked; + } + /** * @return whether the given package name can launch an assist activity. */ @@ -1308,6 +1324,12 @@ && isHomeIntent(intent) && !isResolverOrDelegateActivity()) { && canLaunchAssistActivity(launchedFromPackage)) { activityType = ACTIVITY_TYPE_ASSISTANT; } + if (mResizeMode == -1){ + mResizeMode = info.resizeMode; + } + if (getIsAppLocked()) { + info.resizeMode = RESIZE_MODE_UNRESIZEABLE; + } setActivityType(activityType); } diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index 3b3de4b003e..245e51a79b7 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -64,6 +64,7 @@ import static com.android.server.wm.ActivityStackSupervisor.ON_TOP; import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS; import static com.android.server.wm.ActivityStackSupervisor.TAG_TASKS; +import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_APPLOCK; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CONFIGURATION; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_FOCUS; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_PERMISSIONS_REVIEW; @@ -71,6 +72,7 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STACK; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_TASKS; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_USER_LEAVING; +import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_APPLOCK; import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_CONFIGURATION; import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_FOCUS; import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RESULTS; @@ -144,6 +146,7 @@ class ActivityStarter { private static final String TAG_FOCUS = TAG + POSTFIX_FOCUS; private static final String TAG_CONFIGURATION = TAG + POSTFIX_CONFIGURATION; private static final String TAG_USER_LEAVING = TAG + POSTFIX_USER_LEAVING; + private static final String TAG_APPLOCK = TAG + POSTFIX_APPLOCK; private static final int INVALID_LAUNCH_MODE = -1; private final ActivityTaskManagerService mService; @@ -771,6 +774,15 @@ private int startActivity(IApplicationThread caller, Intent intent, Intent ephem abort |= !mService.getPermissionPolicyInternal().checkStartActivity(intent, callingUid, callingPackage); + final String pkg = aInfo == null ? null : aInfo.packageName; + if (mService.isAppLocked(pkg) && !mService.isAppOpened(pkg) + && !mService.isAlarmOrCallIntent(intent)) { + Slog.d(TAG_APPLOCK, "Locked pkg:" + pkg + " intent:" + intent); + mService.mAppLockService.setAppIntent(pkg, intent); + mService.mAppLockService.launchBeforeActivity(pkg); + abort = true; + } + boolean restrictedBgActivity = false; if (!abort) { try { diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerDebugConfig.java b/services/core/java/com/android/server/wm/ActivityTaskManagerDebugConfig.java index 7f09a071308..864b37cc676 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerDebugConfig.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerDebugConfig.java @@ -32,7 +32,7 @@ public class ActivityTaskManagerDebugConfig { // While debugging it is sometimes useful to have the category name of the log appended to the // base log tag to make sifting through logs with the same base tag easier. By setting this // constant to true, the category name of the log point will be appended to the log tag. - private static final boolean APPEND_CATEGORY_NAME = false; + private static final boolean APPEND_CATEGORY_NAME = true; // Default log tag for the activities. static final String TAG_ATM = "ActivityTaskManager"; @@ -67,6 +67,7 @@ public class ActivityTaskManagerDebugConfig { static final boolean DEBUG_RESULTS = DEBUG_ALL || false; public static final boolean DEBUG_CLEANUP = DEBUG_ALL || false; public static final boolean DEBUG_METRICS = DEBUG_ALL || false; + public static final boolean DEBUG_APPLOCK = DEBUG_ALL || true; static final String POSTFIX_APP = APPEND_CATEGORY_NAME ? "_App" : ""; static final String POSTFIX_CLEANUP = (APPEND_CATEGORY_NAME) ? "_Cleanup" : ""; @@ -89,4 +90,5 @@ public class ActivityTaskManagerDebugConfig { static final String POSTFIX_TRANSITION = APPEND_CATEGORY_NAME ? "_Transition" : ""; static final String POSTFIX_VISIBILITY = APPEND_CATEGORY_NAME ? "_Visibility" : ""; static final String POSTFIX_RESULTS = APPEND_CATEGORY_NAME ? "_Results" : ""; + public static final String POSTFIX_APPLOCK = APPEND_CATEGORY_NAME ? "_AppLock" : ""; } diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index c0075ac4c45..712c2fc7be2 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -28,6 +28,7 @@ import static android.Manifest.permission.START_TASKS_FROM_RECENTS; import static android.Manifest.permission.STOP_APP_SWITCHES; import static android.app.ActivityManager.LOCK_TASK_MODE_NONE; +import static android.app.ActivityManager.START_SUCCESS; import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY; import static android.app.ActivityTaskManager.INVALID_TASK_ID; import static android.app.ActivityTaskManager.RESIZE_MODE_PRESERVE_WINDOW; @@ -342,6 +343,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { Context mContext; + AppLockService mAppLockService; + /** * This Context is themable and meant for UI display (AlertDialogs, etc.). The theme can * change at runtime. Use mContext for non-UI purposes. @@ -705,6 +708,8 @@ public void onSystemReady() { mRecentTasks.onSystemReadyLocked(); mStackSupervisor.onSystemReady(); } + + mAppLockService = LocalServices.getService(AppLockService.class); } public void onInitPowerManagement() { @@ -1492,7 +1497,16 @@ public void startRecentsActivity(Intent intent, @Deprecated IAssistDataReceiver public final int startActivityFromRecents(int taskId, Bundle bOptions) { enforceCallerIsRecentsOrHasPermission(START_TASKS_FROM_RECENTS, "startActivityFromRecents()"); - + final TaskRecord task = mRootActivityContainer.anyTaskForId(taskId); + final ActivityRecord r = task.getTopActivity(false); + if (r != null) { + if (isAppLocked(r.packageName) && !isAppOpened(r.packageName)) { + mAppLockService.setAppIntent(r.packageName, r.intent); + mAppLockService.setStartingFromRecents(); + mAppLockService.launchBeforeActivity(r.packageName); + return ActivityManager.START_SWITCHES_CANCELED; + } + } final int callingPid = Binder.getCallingPid(); final int callingUid = Binder.getCallingUid(); final SafeActivityOptions safeOptions = SafeActivityOptions.fromBundle(bOptions); @@ -1689,6 +1703,15 @@ public final void activityIdle(IBinder token, Configuration config, boolean stop @Override public final void activityResumed(IBinder token) { final long origId = Binder.clearCallingIdentity(); + final ActivityRecord r = ActivityRecord.isInStackLocked(token); + if (r != null) { + if (mAppLockService != null) { + mAppLockService.setForegroundApp(r.packageName); + } + if (isAppLocked(r.packageName)) { + mAppLockService.setAppIntent(r.packageName, r.intent); + } + } synchronized (mGlobalLock) { ActivityRecord.activityResumedLocked(token); mWindowManager.notifyAppResumedFinished(token); @@ -1710,6 +1733,12 @@ public final void activityPaused(IBinder token) { final long origId = Binder.clearCallingIdentity(); synchronized (mGlobalLock) { ActivityStack stack = ActivityRecord.getStackLocked(token); + final ActivityRecord r = ActivityRecord.isInStackLocked(token); + if (r != null) { + if (isAppLocked(r.packageName)) { + mAppLockService.activityStopped(r.packageName, r.intent); + } + } if (stack != null) { stack.activityPausedLocked(token, false); } @@ -3320,6 +3349,9 @@ public void setLockScreenShown(boolean keyguardShowing, boolean aodShowing) { throw new SecurityException("Requires permission " + android.Manifest.permission.DEVICE_POWER); } + if (mAppLockService != null) { + mAppLockService.setKeyguardShown(keyguardShowing); + } synchronized (mGlobalLock) { long ident = Binder.clearCallingIdentity(); @@ -4325,6 +4357,21 @@ public void setSplitScreenResizing(boolean resizing) { } } + boolean isAppLocked(String packageName) { + if (mAppLockService == null || packageName == null) return false; + return mAppLockService.isAppLocked(packageName); + } + + boolean isAppOpened(String packageName) { + if (mAppLockService == null || packageName == null) return true; + return mAppLockService.isAppOpen(packageName); + } + + boolean isAlarmOrCallIntent(Intent intent) { + if (mAppLockService == null) return false; + return mAppLockService.isAlarmOrCallIntent(intent); + } + /** * Check that we have the features required for VR-related API calls, and throw an exception if * not. diff --git a/services/core/java/com/android/server/wm/AppLockService.java b/services/core/java/com/android/server/wm/AppLockService.java new file mode 100644 index 00000000000..b837ea22701 --- /dev/null +++ b/services/core/java/com/android/server/wm/AppLockService.java @@ -0,0 +1,804 @@ +/** + * Copyright (C) 2017-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.server.wm; + +import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_APPLOCK; +import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_APPLOCK; + +import android.app.admin.DevicePolicyManager; +import android.app.ActivityManager; +import android.app.AppOpsManager; +import android.app.IActivityManager; +import android.app.IAppLockService; +import android.app.IAppLockCallback; +import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.database.ContentObserver; +import android.hardware.biometrics.BiometricConstants; +import android.hardware.biometrics.BiometricPrompt; +import android.hardware.biometrics.BiometricPrompt.AuthenticationResult; +import android.net.Uri; +import android.os.Bundle; +import android.os.CancellationSignal; +import android.os.Environment; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.os.RemoteException; +import android.os.UserHandle; +import android.os.UserManager; +import android.provider.Settings; +import android.util.ArrayMap; +import android.util.ArraySet; +import android.util.AtomicFile; +import android.util.Slog; +import android.util.Xml; + +import com.android.internal.os.BackgroundThread; +import com.android.internal.widget.LockPatternUtils; +import com.android.internal.R; +import com.android.server.DisplayThread; +import com.android.server.LocalServices; +import com.android.server.SystemService; + +import libcore.io.IoUtils; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlSerializer; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.ArrayList; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.List; + +public class AppLockService extends SystemService { + + private static final String TAG = "AppLockService"; + private static final String TAG_APPLOCK = TAG + POSTFIX_APPLOCK; + + private static final String FILE_NAME = "locked-apps.xml"; + private static final String TAG_LOCKED_APPS = "locked-apps"; + private static final String TAG_PACKAGE = "package"; + private static final String ATTRIBUTE_NAME = "name"; + private static final String ATTRIBUTE_OP_MODE = "opMode"; + + private final int APPLOCK_TIMEOUT = 15000; + + private AtomicBoolean mEnabled; + private AppLockContainer mCurrent; + private PackageManager mPackageManager; + private AppOpsManager mAppOpsManager; + private CancellationSignal mCancellationSignal; + private BiometricPrompt mBiometricPrompt; + + private UserHandle mUserHandle; + private int mUserId; + private UserManager mUserManager; + private boolean mShowOnlyOnWake; + private boolean mIsSecure; + private boolean mStartingFromRecents; + private boolean mKeyguardShown; + private boolean mLaunchAfterKeyguard; + private boolean mBiometricRunning; + private String mForegroundApp; + private SettingsObserver mSettingsObserver; + + private final LockPatternUtils mLockPatternUtils; + private Context mContext; + + private AtomicFile mFile; + private final AppLockHandler mHandler; + private final Object mLock = new Object(); + + private final ArrayMap mAppsList = new ArrayMap<>(); + private final ArraySet mOpenedApplicationsIndex = new ArraySet<>(); + private final ArraySet mCallbacks= new ArraySet<>(); + + private final BiometricPrompt.AuthenticationCallback mBiometricCallback = + new BiometricPrompt.AuthenticationCallback() { + @Override + public void onAuthenticationError(int errMsgId, CharSequence errString) { + if (DEBUG_APPLOCK && mCurrent != null) Slog.v(TAG, "onAuthenticationError() pkg:" + mCurrent.packageName + + " Id=" + errMsgId + " Name=" + errString); + if (errMsgId == BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED && mStartingFromRecents) { + fallbackToHomeActivity(); + } + mStartingFromRecents = false; + mBiometricRunning = false; + mCurrent = null; + } + + @Override + public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) { + if (DEBUG_APPLOCK) Slog.v(TAG, "onAuthenticationHelp"); + if (DEBUG_APPLOCK) Slog.v(TAG, "Help: Id=" + helpMsgId + " Name=" + helpString); + } + + @Override + public void onAuthenticationFailed() { + if (DEBUG_APPLOCK) Slog.v(TAG, "onAuthenticationFailed"); + mStartingFromRecents = false; + mBiometricRunning = false; + } + + @Override + public void onAuthenticationSucceeded(AuthenticationResult result) { + if (DEBUG_APPLOCK) Slog.v(TAG, "onAuthenticationSucceeded result=" + result); + if (mCurrent != null) { + mCurrent.onUnlockSucceed(); + } + mStartingFromRecents = false; + mBiometricRunning = false; + mCurrent = null; + } + }; + + private final BroadcastReceiver mReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction()) + && !intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) { + if (DEBUG_APPLOCK) Slog.v(TAG_APPLOCK, "Package removed intent received"); + final Uri data = intent.getData(); + if (data == null) { + if (DEBUG_APPLOCK) Slog.v(TAG_APPLOCK, + "Cannot handle package broadcast with null data"); + return; + } + + final String packageName = data.getSchemeSpecificPart(); + removeAppFromList(packageName); + } else if (Intent.ACTION_SCREEN_OFF.equals(intent.getAction())) { + if (DEBUG_APPLOCK) Slog.v(TAG_APPLOCK, "ACTION_SCREEN_OFF"); + clearOpenedAppsList(); + stopBiometricPrompt(); + fallbackToHomeActivity(); + } + } + }; + + public AppLockService(Context context) { + super(context); + + mContext = context; + mHandler = new AppLockHandler(BackgroundThread.getHandler().getLooper()); + mUserId = ActivityManager.getCurrentUser(); + mUserManager = UserManager.get(context); + mEnabled = new AtomicBoolean(!mUserManager.isManagedProfile(mUserId) + && mUserManager.isUserUnlockingOrUnlocked(mUserId)); + mLockPatternUtils = new LockPatternUtils(context); + + IntentFilter packageFilter = new IntentFilter(); + packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); + packageFilter.addDataScheme("package"); + context.registerReceiver(mReceiver, packageFilter); + + IntentFilter intentFilter = new IntentFilter(); + intentFilter.addAction(Intent.ACTION_SCREEN_OFF); + context.registerReceiver(mReceiver, intentFilter); + + mSettingsObserver = new SettingsObserver(mHandler); + mSettingsObserver.observe(); + + mHandler.sendEmptyMessage(AppLockHandler.MSG_READ_STATE); + } + + @Override + public void onStart() { + if (DEBUG_APPLOCK) Slog.v(TAG_APPLOCK, "Starting AppLockService"); + publishBinderService(Context.APPLOCK_SERVICE, new AppLockImpl()); + publishLocalService(AppLockService.class, this); + } + + @Override + public void onUnlockUser(int userHandle) { + if (DEBUG_APPLOCK) Slog.v(TAG_APPLOCK, "onUnlockUser()"); + mUserId = userHandle; + mPackageManager = mContext.getPackageManager(); + mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE); + mHandler.sendEmptyMessage(AppLockHandler.MSG_INIT_APPS); + } + + @Override + public void onSwitchUser(int userHandle) { + if (DEBUG_APPLOCK) Slog.v(TAG_APPLOCK, "onSwitchUser()"); + mUserId = userHandle; + mHandler.sendEmptyMessage(AppLockHandler.MSG_INIT_APPS); + } + + @Override + public void onStopUser(int userHandle) { + if (DEBUG_APPLOCK) Slog.v(TAG_APPLOCK, "onStopUser()"); + mEnabled.set(false); + } + + private void initLockedApps() { + if (DEBUG_APPLOCK) Slog.v(TAG_APPLOCK, "initLockedApps(" + mUserId + ")"); + mUserHandle = new UserHandle(mUserId); + if (mUserManager.isManagedProfile(mUserId)) { + if (DEBUG_APPLOCK) Slog.v(TAG_APPLOCK, "Disabled"); + mEnabled.set(false); + } else { + mFile = new AtomicFile(getFile()); + if (DEBUG_APPLOCK) Slog.v(TAG_APPLOCK, "Enabled"); + mEnabled.set(true); + readState(); + clearOpenedAppsList(); + } + mIsSecure = isSecure(); + } + + private File getFile() { + File file = new File(Environment.getDataSystemCeDirectory(mUserId), FILE_NAME); + if (DEBUG_APPLOCK) Slog.v(TAG_APPLOCK, "getFile(): " + file.getAbsolutePath()); + return file; + } + + private void readState() { + if (DEBUG_APPLOCK) Slog.v(TAG_APPLOCK, "readState()"); + if (!mEnabled.get()) { + return; + } + try (FileInputStream in = mFile.openRead()) { + XmlPullParser parser = Xml.newPullParser(); + parser.setInput(in, null); + parseXml(parser); + if (DEBUG_APPLOCK) Slog.v(TAG_APPLOCK, "Read locked-apps.xml successfully"); + } catch (FileNotFoundException e) { + if (DEBUG_APPLOCK) Slog.v(TAG_APPLOCK, "locked-apps.xml not found"); + Slog.i(TAG, "locked-apps.xml not found"); + } catch (XmlPullParserException | IOException e) { + throw new IllegalStateException("Failed to parse locked-apps.xml: " + mFile, e); + } + } + + private void parseXml(XmlPullParser parser) throws IOException, + XmlPullParserException { + int type; + int depth; + int innerDepth = parser.getDepth() + 1; + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && ((depth = parser.getDepth()) >= innerDepth || type != XmlPullParser.END_TAG)) { + if (depth > innerDepth || type != XmlPullParser.START_TAG) { + continue; + } + if (parser.getName().equals(TAG_LOCKED_APPS)) { + parsePackages(parser); + return; + } + } + Slog.w(TAG, "Missing <" + TAG_LOCKED_APPS + "> in locked-apps.xml"); + } + + private void parsePackages(XmlPullParser parser) throws IOException, + XmlPullParserException { + mAppsList.clear(); + int type; + int depth; + int innerDepth = parser.getDepth() + 1; + boolean writeAfter = false; + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && ((depth = parser.getDepth()) >= innerDepth || type != XmlPullParser.END_TAG)) { + if (depth > innerDepth || type != XmlPullParser.START_TAG) { + continue; + } + if (parser.getName().equals(TAG_PACKAGE)) { + String pkgName = parser.getAttributeValue(null, ATTRIBUTE_NAME); + String appOpMode = parser.getAttributeValue(null, ATTRIBUTE_OP_MODE); + AppLockContainer cont = new AppLockContainer(pkgName, (appOpMode == null) + ? -1 : Integer.parseInt(appOpMode)); + writeAfter = (appOpMode == null) || (Integer.parseInt(appOpMode) == -1); + mAppsList.put(pkgName, cont); + if (DEBUG_APPLOCK) Slog.v(TAG_APPLOCK, "parsePackages(): pkgName=" + pkgName + + " appOpMode=" + appOpMode); + } + } + if (writeAfter) { + if (DEBUG_APPLOCK) Slog.v(TAG_APPLOCK, "parsePackages(): writeAfter"); + mHandler.sendEmptyMessage(AppLockHandler.MSG_WRITE_STATE); + } + } + + private void writeState() { + if (DEBUG_APPLOCK) Slog.v(TAG_APPLOCK, "writeState()"); + if (!mEnabled.get()) { + return; + } + + FileOutputStream out = null; + try { + out = mFile.startWrite(); + XmlSerializer serializer = Xml.newSerializer(); + serializer.setOutput(out, StandardCharsets.UTF_8.name()); + serializer.setFeature( + "http://xmlpull.org/v1/doc/features.html#indent-output", true); + serializer.startDocument(null, true); + serializeLockedApps(serializer); + serializer.endDocument(); + mFile.finishWrite(out); + if (DEBUG_APPLOCK) Slog.v(TAG_APPLOCK, "Wrote locked-apps.xml successfully"); + } catch (IllegalArgumentException | IllegalStateException | IOException e) { + Slog.wtf(TAG, "Failed to write locked-apps.xml, restoring backup", e); + if (out != null) { + mFile.failWrite(out); + } + } finally { + IoUtils.closeQuietly(out); + } + } + + private void serializeLockedApps(XmlSerializer serializer) throws IOException { + serializer.startTag(null, TAG_LOCKED_APPS); + for (int i = 0; i < mAppsList.size(); ++i) { + AppLockContainer cont = mAppsList.valueAt(i); + serializer.startTag(null, TAG_PACKAGE); + serializer.attribute(null, ATTRIBUTE_NAME, cont.packageName); + serializer.attribute(null, ATTRIBUTE_OP_MODE, String.valueOf(cont.appOpMode)); + serializer.endTag(null, TAG_PACKAGE); + } + serializer.endTag(null, TAG_LOCKED_APPS); + } + + private void addAppToList(String packageName) { + if (!mEnabled.get()) { + return; + } + if (DEBUG_APPLOCK) Slog.v(TAG, "addAppToList packageName:" + packageName); + if (!mAppsList.containsKey(packageName)) { + AppLockContainer cont = new AppLockContainer(packageName, -1); + mAppsList.put(packageName, cont); + mHandler.sendEmptyMessage(AppLockHandler.MSG_WRITE_STATE); + dispatchCallbacks(packageName, false); + } + } + + private void removeAppFromList(String packageName) { + if (!mEnabled.get()) { + return; + } + if (mAppsList.containsKey(packageName)) { + AppLockContainer cont = getAppLockContainer(packageName); + cont.appRemovedFromList(); + mAppsList.remove(packageName); + mHandler.sendEmptyMessage(AppLockHandler.MSG_WRITE_STATE); + dispatchCallbacks(packageName, true); + } + } + + public void reportPasswordChanged(int userId) { + if (mUserId == userId) { + mIsSecure = isSecure(); + if (DEBUG_APPLOCK) Slog.v(TAG_APPLOCK, "reportPasswordChanged() mIsSecure:" + mIsSecure); + } + } + + public boolean isAppLocked(String packageName) { + if (!mEnabled.get() || !mIsSecure) { + return false; + } + return mAppsList.containsKey(packageName); + } + + private AppLockContainer getAppLockContainer(String packageName) { + if (!mEnabled.get()) { + return null; + } + return mAppsList.get(packageName); + } + + private void clearOpenedAppsList() { + if (DEBUG_APPLOCK) Slog.v(TAG_APPLOCK, "clearOpenedAppsList()"); + for (String p : mOpenedApplicationsIndex) { + dispatchCallbacks(p, false); + } + mOpenedApplicationsIndex.clear(); + } + + public boolean isAppOpen(String packageName) { + return mOpenedApplicationsIndex.contains(packageName); + } + + private List getLockedPackages() { + return new ArrayList(mAppsList.keySet()); + } + + public boolean isAlarmOrCallIntent(Intent intent) { + if (intent == null) return false; + if (DEBUG_APPLOCK) Slog.v(TAG_APPLOCK, "isAlarmOrCallIntent() intent:" + intent); + + String intentClassName = intent.getComponent().getClassName().toLowerCase(); + return (intentClassName.contains("call") || intentClassName.contains("voip") + || intentClassName.contains("alarm")) + && intentClassName.contains("activity"); + } + + void removeOpenedApp(String packageName) { + if (isAppOpen(packageName)) { + if (DEBUG_APPLOCK) Slog.v(TAG_APPLOCK, "removeOpenedApp(" + packageName + ")"); + mOpenedApplicationsIndex.remove(packageName); + dispatchCallbacks(packageName, false); + } + } + + void addOpenedApp(String packageName) { + if (DEBUG_APPLOCK) Slog.v(TAG_APPLOCK, "addOpenedApp(" + packageName + ")"); + mOpenedApplicationsIndex.add(packageName); + } + + public void launchBeforeActivity(String packageName) { + if (DEBUG_APPLOCK) Slog.v(TAG_APPLOCK, "launchBeforeActivity(" + packageName + ")"); + AppLockContainer cont = getAppLockContainer(packageName); + if (cont != null) { + DisplayThread.getHandler().post(() -> { + mCurrent = cont; + if (mKeyguardShown) { + mLaunchAfterKeyguard = true; + return; + } + mBiometricPrompt = new BiometricPrompt.Builder(mContext) + .setTitle(cont.appLabel) + .setApplockPackage(cont.packageName) + .setDescription(cont.appLabel) + .setDeviceCredentialAllowed(true) + .setConfirmationRequired(false) + .build(); + startBiometricPrompt(); + }); + } + } + + public void activityStopped(String packageName, Intent removed) { + AppLockContainer cont = getAppLockContainer(packageName); + if (cont != null) { + if (DEBUG_APPLOCK) Slog.v(TAG_APPLOCK, "activityStopped() pkg:" + packageName); + if (isAppOpen(packageName)) { + mHandler.removeMessages(AppLockHandler.MSG_REMOVE_OPENED_APP, cont.packageName); + if (cont.intent.equals(removed)) { + if (DEBUG_APPLOCK) Slog.v(TAG_APPLOCK, "activityStopped() send: MSG_REMOVE_OPENED_APP"); + final Message msgRemove = mHandler.obtainMessage(AppLockHandler.MSG_REMOVE_OPENED_APP, + cont.packageName); + mHandler.sendMessageDelayed(msgRemove, APPLOCK_TIMEOUT); + } + } + } + } + + public void setAppIntent(String packageName, Intent intent) { + if (DEBUG_APPLOCK) Slog.v(TAG, "setAppIntent(" + packageName + ") intent:" + intent); + AppLockContainer cont = getAppLockContainer(packageName); + if (cont != null) { + cont.intent = intent; + mHandler.removeMessages(AppLockHandler.MSG_REMOVE_OPENED_APP, cont.packageName); + } + } + + public void setStartingFromRecents() { + if (DEBUG_APPLOCK) Slog.v(TAG, "setStartingFromRecents()"); + mStartingFromRecents = true; + } + + public void setForegroundApp(String packageName) { + if (DEBUG_APPLOCK) Slog.v(TAG, "setForegroundApp(" + packageName + ")"); + mForegroundApp = packageName; + } + + private void startBiometricPrompt() { + if (DEBUG_APPLOCK) Slog.v(TAG, "startBiometricPrompt()"); + if (mBiometricRunning) return; + if (mCancellationSignal == null || mCancellationSignal.isCanceled()) { + mCancellationSignal = new CancellationSignal(); + } + mBiometricPrompt.authenticate(mCancellationSignal, mContext.getMainExecutor(), mBiometricCallback); + mBiometricRunning = true; + } + + private void stopBiometricPrompt() { + if (DEBUG_APPLOCK) Slog.v(TAG, "stopBiometricPrompt()"); + if (mCancellationSignal != null) { + mCancellationSignal.cancel(); + } + mBiometricRunning = false; + } + + public void setKeyguardShown(boolean shown) { + mKeyguardShown = shown; + if (mLaunchAfterKeyguard && !shown) { + mLaunchAfterKeyguard = false; + mHandler.postDelayed(() -> { + if (mCurrent != null) { + launchBeforeActivity(mCurrent.packageName); + } + }, 150); + } + } + + private boolean isSecure() { + int storedQuality = mLockPatternUtils.getKeyguardStoredPasswordQuality(mUserId); + switch (storedQuality) { + case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING: + case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC: + case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX: + case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC: + case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC: + case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX: + case DevicePolicyManager.PASSWORD_QUALITY_MANAGED: + return true; + default: + return false; + } + } + + private void fallbackToHomeActivity() { + if (DEBUG_APPLOCK) Slog.v(TAG_APPLOCK, "fallbackToHomeActivity()"); + if (mAppsList.containsKey(mForegroundApp) || mStartingFromRecents) { + Intent intent = new Intent(Intent.ACTION_MAIN); + intent.addCategory(Intent.CATEGORY_HOME); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + mContext.startActivityAsUser(intent, mUserHandle); + } + } + + private int getLockedAppsCount() { + if (DEBUG_APPLOCK) Slog.v(TAG_APPLOCK, "Number of locked apps: " + mAppsList.size()); + return mIsSecure ? mAppsList.size() : 0; + } + + private void dispatchCallbacks(String packageName, boolean opened) { + mHandler.post(() -> { + synchronized (mCallbacks) { + final int N = mCallbacks.size(); + boolean cleanup = false; + for (int i = 0; i < N; i++) { + final IAppLockCallback callback = mCallbacks.valueAt(i); + try { + if (callback != null) { + callback.onAppStateChanged(packageName, opened); + } else { + cleanup = true; + } + } catch (RemoteException e) { + cleanup = true; + } + } + if (cleanup) { + cleanUpCallbacksLocked(null); + } + } + }); + } + + private void cleanUpCallbacksLocked(IAppLockCallback callback) { + mHandler.post(() -> { + synchronized (mCallbacks) { + for (int i = mCallbacks.size() - 1; i >= 0; i--) { + IAppLockCallback found = mCallbacks.valueAt(i); + if (found == null || found == callback) { + mCallbacks.remove(i); + } + } + } + }); + } + + private void addAppLockCallback(IAppLockCallback callback) { + mHandler.post(() -> { + synchronized(mCallbacks) { + if (!mCallbacks.contains(callback)) { + mCallbacks.add(callback); + } + } + }); + } + + private void removeAppLockCallback(IAppLockCallback callback) { + mHandler.post(() -> { + synchronized(mCallbacks) { + if (mCallbacks.contains(callback)) { + mCallbacks.remove(callback); + } + } + }); + } + + private class SettingsObserver extends ContentObserver { + + SettingsObserver(Handler handler) { + super(handler); + } + + void observe() { + ContentResolver resolver = mContext.getContentResolver(); + resolver.registerContentObserver(Settings.System.getUriFor( + Settings.System.APP_LOCK_SHOW_ONLY_ON_WAKE), false, this, + UserHandle.USER_ALL); + mShowOnlyOnWake = Settings.System.getIntForUser(mContext + .getContentResolver(), + Settings.System.APP_LOCK_SHOW_ONLY_ON_WAKE, 0, + mUserId) != 0; + } + + @Override + public void onChange(boolean selfChange) { + mShowOnlyOnWake = Settings.System.getIntForUser(mContext + .getContentResolver(), + Settings.System.APP_LOCK_SHOW_ONLY_ON_WAKE, 0, + mUserId) != 0; + clearOpenedAppsList(); + } + } + + private class AppLockImpl extends IAppLockService.Stub { + @Override + public void addAppToList(String packageName) { + AppLockService.this.addAppToList(packageName); + } + + @Override + public void removeAppFromList(String packageName) { + AppLockService.this.removeAppFromList(packageName); + } + + @Override + public boolean isAppLocked(String packageName) { + return AppLockService.this.isAppLocked(packageName); + } + + @Override + public boolean isAppOpen(String packageName) { + return AppLockService.this.isAppOpen(packageName); + } + + @Override + public int getLockedAppsCount() { + return AppLockService.this.getLockedAppsCount(); + } + + @Override + public List getLockedPackages() { + return AppLockService.this.getLockedPackages(); + } + + @Override + public void addAppLockCallback(IAppLockCallback callback) { + AppLockService.this.addAppLockCallback(callback); + } + + @Override + public void removeAppLockCallback(IAppLockCallback callback) { + AppLockService.this.removeAppLockCallback(callback); + } + }; + + private class AppLockHandler extends Handler { + + public static final int MSG_INIT_APPS = 0; + public static final int MSG_READ_STATE = 1; + public static final int MSG_WRITE_STATE = 2; + public static final int MSG_REMOVE_OPENED_APP = 3; + + public AppLockHandler(Looper looper) { + super(looper); + } + + @Override + public void handleMessage(android.os.Message msg) { + switch (msg.what) { + case MSG_INIT_APPS: + initLockedApps(); + break; + case MSG_READ_STATE: + readState(); + break; + case MSG_WRITE_STATE: + writeState(); + break; + case MSG_REMOVE_OPENED_APP: + if (!mShowOnlyOnWake) { + removeOpenedApp((String) msg.obj); + } + break; + default: + Slog.w(TAG, "Unknown message:" + msg.what); + } + } + } + + private class AppLockContainer { + private final String packageName; + private ApplicationInfo aInfo; + private CharSequence appLabel; + private int appOpMode = -1; + private Intent intent; + + public AppLockContainer(String pkg, int opMode) { + packageName = pkg; + try { + aInfo = mPackageManager.getApplicationInfo(packageName, 0); + } catch(PackageManager.NameNotFoundException e) { + Slog.e(TAG, "Failed to find package " + packageName, e); + removeAppFromList(packageName); + return; + } + appLabel = mPackageManager.getApplicationLabel(aInfo); + + if (opMode == -1) { + appOpMode = mAppOpsManager.checkOpNoThrow(AppOpsManager.OP_SYSTEM_ALERT_WINDOW, + aInfo.uid, packageName); + } else { + appOpMode = opMode; + } + + if (appOpMode == AppOpsManager.MODE_ALLOWED + || appOpMode == AppOpsManager.MODE_DEFAULT) { + mAppOpsManager.setMode(AppOpsManager.OP_SYSTEM_ALERT_WINDOW, + aInfo.uid, packageName, AppOpsManager.MODE_ERRORED); + } + } + + private void startActivityAfterUnlock() { + if (DEBUG_APPLOCK) Slog.v(TAG, "startActivityAfterUnlock() intent:" + intent); + Intent fallBackIntent = new Intent(Intent.ACTION_MAIN); + fallBackIntent.setPackage(packageName); + List resolveInfos = mPackageManager.queryIntentActivities(fallBackIntent, 0); + if(resolveInfos.size() > 0) { + ResolveInfo launchable = resolveInfos.get(0); + ActivityInfo activity = launchable.activityInfo; + ComponentName name = new ComponentName(activity.applicationInfo.packageName, + activity.name); + fallBackIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | + Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); + fallBackIntent.setComponent(name); + } + if (intent != null) { + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + mContext.startActivityAsUser(intent, mUserHandle); + } else if (fallBackIntent != null) { + mContext.startActivityAsUser(fallBackIntent, mUserHandle); + } + if (fallBackIntent != null) { + intent = fallBackIntent; + } + } + + private void onUnlockSucceed() { + addOpenedApp(packageName); + startActivityAfterUnlock(); + dispatchCallbacks(packageName, true); + } + + private void appRemovedFromList() { + Slog.d(TAG, "appRemovedFromList() appOpMode: " + appOpMode); + mAppOpsManager.setMode(AppOpsManager.OP_SYSTEM_ALERT_WINDOW, + aInfo.uid, packageName, appOpMode); + } + } +} diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java index f1a9e60d18a..3a84230b10e 100644 --- a/services/core/java/com/android/server/wm/AppWindowToken.java +++ b/services/core/java/com/android/server/wm/AppWindowToken.java @@ -2459,7 +2459,7 @@ boolean canTurnScreenOn() { */ boolean shouldUseAppThemeSnapshot() { return mDisablePreviewScreenshots || forAllWindows(w -> (w.mAttrs.flags & FLAG_SECURE) != 0, - true /* topToBottom */); + true /* topToBottom */) || mActivityRecord.getIsAppLocked(); } SurfaceControl getAppAnimationLayer() { diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 0d219c11af8..c4330d595e1 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -157,6 +157,7 @@ import com.android.server.vr.VrManagerService; import com.android.server.webkit.WebViewUpdateService; import com.android.server.wm.ActivityTaskManagerService; +import com.android.server.wm.AppLockService; import com.android.server.wm.WindowManagerGlobalLock; import com.android.server.wm.WindowManagerService; @@ -1099,6 +1100,10 @@ private void startOtherServices() { mSystemServiceManager.startService(ActivityTriggerService.class); traceEnd(); + traceBeginAndSlog("StartAppLockService"); + mSystemServiceManager.startService(AppLockService.class); + traceEnd(); + traceBeginAndSlog("SignedConfigService"); SignedConfigService.registerUpdateReceiver(mSystemContext); traceEnd(); From a22f22937a682e111e1a9871acf9ad617d94c7cf Mon Sep 17 00:00:00 2001 From: rituj Date: Sat, 26 Sep 2020 19:27:39 +0530 Subject: [PATCH 16/26] BiometricPrompt: Force portrait orientation if we have FOD Signed-off-by: rituj Change-Id: I2a53c9894ee821aed74d6a91a59346568eaf230a --- .../com/android/systemui/biometrics/BiometricDialogView.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java index 2870fea51ea..8bea276764e 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java @@ -23,6 +23,7 @@ import android.app.admin.DevicePolicyManager; import android.content.Context; +import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.res.Configuration; @@ -668,6 +669,9 @@ public WindowManager.LayoutParams getLayoutParams() { | WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS; lp.setTitle("BiometricDialogView"); lp.token = mWindowToken; + if (mHasFod) { + lp.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; + } return lp; } From 2e9d4417edbff034ecbcb242ce75cc5661335dce Mon Sep 17 00:00:00 2001 From: rituj Date: Sat, 25 Jul 2020 15:26:55 +0530 Subject: [PATCH 17/26] base: AppLock v1.1 * Add support for per-app basis notification redaction * Fix applock on managed profiles Signed-off-by: rituj Change-Id: I2ba9a0e4996e000a4e4c932aa15f821cb1bcf680 --- core/java/android/app/AppLockManager.java | 34 ++- core/java/android/app/IAppLockCallback.aidl | 2 +- core/java/android/app/IAppLockService.aidl | 8 + core/java/android/provider/Settings.java | 8 - .../NotificationViewHierarchyManager.java | 7 +- .../NotificationEntryManager.java | 54 +--- .../collection/NotificationRowBinderImpl.java | 2 +- .../row/ExpandableNotificationRow.java | 20 +- .../com/android/server/wm/AppLockService.java | 258 +++++++++++------- 9 files changed, 219 insertions(+), 174 deletions(-) diff --git a/core/java/android/app/AppLockManager.java b/core/java/android/app/AppLockManager.java index c980e091af5..8dd25e5c34e 100644 --- a/core/java/android/app/AppLockManager.java +++ b/core/java/android/app/AppLockManager.java @@ -72,6 +72,22 @@ public boolean isAppOpen(String packageName) { } } + public void setShowOnlyOnWake(boolean showOnce) { + try { + mService.setShowOnlyOnWake(showOnce); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + public boolean getShowOnlyOnWake() { + try { + return mService.getShowOnlyOnWake(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + public int getLockedAppsCount() { try { return mService.getLockedAppsCount(); @@ -88,6 +104,22 @@ public List getLockedPackages() { } } + public boolean getAppNotificationHide(String packageName) { + try { + return mService.getAppNotificationHide(packageName); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + public void setAppNotificationHide(String packageName, boolean hide) { + try { + mService.setAppNotificationHide(packageName, hide); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + public void addAppLockCallback(IAppLockCallback c) { try { mService.addAppLockCallback(c); @@ -106,6 +138,6 @@ public void removeAppLockCallback(IAppLockCallback c) { public abstract static class AppLockCallback extends IAppLockCallback.Stub { @Override - public abstract void onAppStateChanged(String pkg, boolean opened); + public abstract void onAppStateChanged(String pkg); }; } diff --git a/core/java/android/app/IAppLockCallback.aidl b/core/java/android/app/IAppLockCallback.aidl index 1719613b8e3..b03d8b65181 100644 --- a/core/java/android/app/IAppLockCallback.aidl +++ b/core/java/android/app/IAppLockCallback.aidl @@ -19,5 +19,5 @@ package android.app; /** @hide */ oneway interface IAppLockCallback { - void onAppStateChanged(String packageName, boolean opened); + void onAppStateChanged(String packageName); } \ No newline at end of file diff --git a/core/java/android/app/IAppLockService.aidl b/core/java/android/app/IAppLockService.aidl index 30c8c4010ea..3a5c9ab8a36 100644 --- a/core/java/android/app/IAppLockService.aidl +++ b/core/java/android/app/IAppLockService.aidl @@ -29,10 +29,18 @@ interface IAppLockService { boolean isAppOpen(in String packageName); + void setShowOnlyOnWake(in boolean showOnce); + + boolean getShowOnlyOnWake(); + int getLockedAppsCount(); List getLockedPackages(); + boolean getAppNotificationHide(in String packageName); + + void setAppNotificationHide(in String packageName, in boolean hide); + void addAppLockCallback(IAppLockCallback callback); void removeAppLockCallback(IAppLockCallback callback); diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 59b109adae8..17f9c8c8860 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -3577,14 +3577,6 @@ public boolean validate(@Nullable String value) { */ public static final String APP_LOCK_SHOW_ONLY_ON_WAKE = "app_lock_show_only_on_wake"; - - /** - * Indicates whether we should only show the app lock view when the device is woken up - * Or always. - * @hide - */ - public static final String APP_LOCK_HIDE_NOTIFICATIONS = "app_lock_hide_notifications"; - /** * Control whether to enable adaptive sleep mode. * @hide diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java index 66a06193d0b..646281f5843 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java @@ -167,11 +167,12 @@ public void updateNotificationViews() { || !mLockscreenUserManager.needsSeparateWorkChallenge(userId))) { userPublic = false; } - boolean needsRedaction = mLockscreenUserManager.needsRedaction(ent); + boolean appLocked = ent.getRow().isAppLocked(); + boolean needsRedaction = appLocked || mLockscreenUserManager.needsRedaction(ent); boolean sensitive = userPublic && needsRedaction; boolean deviceSensitive = devicePublic - && !mLockscreenUserManager.userAllowsPrivateNotificationsInPublic( - currentUserId); + && (appLocked || !mLockscreenUserManager.userAllowsPrivateNotificationsInPublic( + currentUserId)); ent.setSensitive(sensitive, deviceSensitive); ent.getRow().setNeedsRedaction(needsRedaction); if (mGroupManager.isChildInGroupWithSummary(ent.notification)) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java index c2893a232df..a96f6d2672b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java @@ -22,12 +22,8 @@ import android.app.AppLockManager; import android.app.AppLockManager.AppLockCallback; import android.app.Notification; -import android.content.ContentResolver; import android.content.Context; -import android.database.ContentObserver; import android.os.Bundle; -import android.os.UserHandle; -import android.provider.Settings; import android.service.notification.NotificationListenerService; import android.service.notification.StatusBarNotification; import android.util.ArrayMap; @@ -107,17 +103,15 @@ public class NotificationEntryManager implements private final List mNotificationEntryListeners = new ArrayList<>(); private NotificationRemoveInterceptor mRemoveInterceptor; - private SettingsObserver mSettingsObserver; - private boolean mLockNotifications; private final AppLockManager mAppLockManager; private final AppLockCallback mAppLockCallback = new AppLockCallback() { @Override - public void onAppStateChanged(String pkg, boolean open) { - updateAppNotifications(pkg, open); + public void onAppStateChanged(String pkg) { + updateAppNotifications(pkg); } }; - private void updateAppNotifications(String pkg, boolean open) { + private void updateAppNotifications(String pkg) { ArrayList arr = mNotificationData.getAllNotificationsForPackage(pkg); for (NotificationEntry notif : arr) { if (notif.rowExists()) { @@ -125,7 +119,8 @@ private void updateAppNotifications(String pkg, boolean open) { boolean appLocked = mAppLockManager.isAppLocked(pkg); row.setAppLocked(appLocked); Dependency.get(Dependency.MAIN_HANDLER).post(() -> { - row.onAppStateChanged(!mLockNotifications || open); + row.onAppStateChanged(!mAppLockManager.getAppNotificationHide(pkg) + || mAppLockManager.isAppOpen(pkg)); }); } } @@ -159,8 +154,6 @@ public NotificationEntryManager(Context context) { mNotificationData = new NotificationData(); mAppLockManager = (AppLockManager) context.getSystemService(Context.APPLOCK_SERVICE); mAppLockManager.addAppLockCallback(mAppLockCallback); - mSettingsObserver = new SettingsObserver(context); - mSettingsObserver.observe(); } /** Adds a {@link NotificationEntryListener}. */ @@ -279,8 +272,8 @@ public void onAsyncInflationFinished(NotificationEntry entry, ExpandableNotificationRow row = entry.getRow(); row.setAppLocked(isAppLocked); Dependency.get(Dependency.MAIN_HANDLER).post(() -> { - row.onAppStateChanged(!mLockNotifications || - mAppLockManager.isAppOpen(pkg)); + row.onAppStateChanged(!mAppLockManager.getAppNotificationHide(pkg) || + mAppLockManager.isAppOpen(pkg)); }); } boolean isNew = mNotificationData.get(entry.key) == null; @@ -596,37 +589,4 @@ private NotificationRowBinder requireBinder() { } return mNotificationRowBinder; } - - private class SettingsObserver extends ContentObserver { - - private ContentResolver mCr; - - SettingsObserver(Context context) { - super(Dependency.get(Dependency.MAIN_HANDLER)); - mCr = context.getContentResolver(); - } - - void observe() { - mCr.registerContentObserver(Settings.System.getUriFor( - Settings.System.APP_LOCK_HIDE_NOTIFICATIONS), false, this, - UserHandle.USER_ALL); - mLockNotifications = Settings.System.getIntForUser(mCr, - Settings.System.APP_LOCK_HIDE_NOTIFICATIONS, 1, - UserHandle.USER_CURRENT) != 0; - } - - @Override - public void onChange(boolean selfChange) { - boolean lockNotifications = Settings.System.getIntForUser(mCr, - Settings.System.APP_LOCK_HIDE_NOTIFICATIONS, 1, - UserHandle.USER_CURRENT) != 0; - if (mLockNotifications != lockNotifications) { - mLockNotifications = lockNotifications; - List lockedPackages = mAppLockManager.getLockedPackages(); - for (String pkg : lockedPackages) { - updateAppNotifications(pkg, mAppLockManager.isAppOpen(pkg)); - } - } - } - } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.java index 240d567b78c..04643d7c003 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.java @@ -259,7 +259,7 @@ private void updateNotification( if (mNotificationInterruptionStateProvider.shouldHeadsUp(entry)) { row.updateInflationFlag(FLAG_CONTENT_VIEW_HEADS_UP, true /* shouldInflate */); } - row.setNeedsRedaction( + row.setNeedsRedaction(row.isAppLocked() || Dependency.get(NotificationLockscreenUserManager.class).needsRedaction(entry)); row.inflateViews(); 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 6ec3a60b436..608c852b61c 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 @@ -47,8 +47,6 @@ import android.os.Build; import android.os.Bundle; import android.os.SystemClock; -import android.os.UserHandle; -import android.provider.Settings; import android.service.notification.StatusBarNotification; import android.util.ArraySet; import android.util.AttributeSet; @@ -458,14 +456,11 @@ public void setEntry(@NonNull NotificationEntry entry) { updateAlarmOrCall(); AppLockManager appLockManager = (AppLockManager) mContext .getSystemService(Context.APPLOCK_SERVICE); - String pkg = mStatusBarNotification.getPackageName(); + final String pkg = mStatusBarNotification.getPackageName(); mIsAppLocked = appLockManager.isAppLocked(pkg); if (mIsAppLocked) { - boolean lockNotifications = Settings.System.getIntForUser( - mContext.getContentResolver(), - Settings.System.APP_LOCK_HIDE_NOTIFICATIONS, 1, - UserHandle.USER_CURRENT) != 0; - mAppOpen = appLockManager.isAppOpen(pkg) || !lockNotifications; + mAppOpen = appLockManager.isAppOpen(pkg) || !appLockManager + .getAppNotificationHide(pkg); } if (mIsAppLocked) Log.d(TAG, "setEntry() app:" + mAppName + " mAppOpen:" + mAppOpen + " mIsAlarmOrCall:" + mIsAlarmOrCall); @@ -1640,7 +1635,6 @@ public void setInflationCallback(InflationCallback callback) { } public void setNeedsRedaction(boolean needsRedaction) { - needsRedaction |= mIsAppLocked && !mAppOpen; if (mNeedsRedaction != needsRedaction) { mNeedsRedaction = needsRedaction; updateInflationFlag(FLAG_CONTENT_VIEW_PUBLIC, needsRedaction /* shouldInflate */); @@ -2604,7 +2598,6 @@ public void run() { public void onAppStateChanged(boolean open) { if (mAppOpen != open) { mAppOpen = open; - setNeedsRedaction(mNeedsRedaction); setHideSensitive(mSensitive, true, 0, 100); } } @@ -2633,9 +2626,10 @@ private void updateAlarmOrCall() { ComponentName cmp = intent.getIntent().getComponent(); if (cmp != null) { String intentClassName = cmp.getClassName().toLowerCase(); - mIsAlarmOrCall = (intentClassName.contains("call") || intentClassName.contains("voip") - || intentClassName.contains("alarm")) - && intentClassName.contains("activity"); + mIsAlarmOrCall = intentClassName.contains("callactivity") + || intentClassName.contains("callingactivity") + || intentClassName.contains("voipactivity") + || intentClassName.contains("alarmactivity"); } else { intent = mStatusBarNotification.getNotification().fullScreenIntent; if (intent != null) { diff --git a/services/core/java/com/android/server/wm/AppLockService.java b/services/core/java/com/android/server/wm/AppLockService.java index b837ea22701..3041e333cc3 100644 --- a/services/core/java/com/android/server/wm/AppLockService.java +++ b/services/core/java/com/android/server/wm/AppLockService.java @@ -21,25 +21,26 @@ import android.app.admin.DevicePolicyManager; import android.app.ActivityManager; +import android.app.AppGlobals; import android.app.AppOpsManager; import android.app.IActivityManager; import android.app.IAppLockService; import android.app.IAppLockCallback; import android.content.BroadcastReceiver; import android.content.ComponentName; -import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; +import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; -import android.database.ContentObserver; import android.hardware.biometrics.BiometricConstants; import android.hardware.biometrics.BiometricPrompt; import android.hardware.biometrics.BiometricPrompt.AuthenticationResult; import android.net.Uri; +import android.os.Binder; import android.os.Bundle; import android.os.CancellationSignal; import android.os.Environment; @@ -90,12 +91,13 @@ public class AppLockService extends SystemService { private static final String TAG_PACKAGE = "package"; private static final String ATTRIBUTE_NAME = "name"; private static final String ATTRIBUTE_OP_MODE = "opMode"; + private static final String ATTRIBUTE_NOTIFICATION = "notifHide"; private final int APPLOCK_TIMEOUT = 15000; - private AtomicBoolean mEnabled; private AppLockContainer mCurrent; private PackageManager mPackageManager; + private IPackageManager mIpm; private AppOpsManager mAppOpsManager; private CancellationSignal mCancellationSignal; private BiometricPrompt mBiometricPrompt; @@ -110,8 +112,7 @@ public class AppLockService extends SystemService { private boolean mLaunchAfterKeyguard; private boolean mBiometricRunning; private String mForegroundApp; - private SettingsObserver mSettingsObserver; - + private final LockPatternUtils mLockPatternUtils; private Context mContext; @@ -192,9 +193,6 @@ public AppLockService(Context context) { mContext = context; mHandler = new AppLockHandler(BackgroundThread.getHandler().getLooper()); mUserId = ActivityManager.getCurrentUser(); - mUserManager = UserManager.get(context); - mEnabled = new AtomicBoolean(!mUserManager.isManagedProfile(mUserId) - && mUserManager.isUserUnlockingOrUnlocked(mUserId)); mLockPatternUtils = new LockPatternUtils(context); IntentFilter packageFilter = new IntentFilter(); @@ -205,11 +203,6 @@ public AppLockService(Context context) { IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(Intent.ACTION_SCREEN_OFF); context.registerReceiver(mReceiver, intentFilter); - - mSettingsObserver = new SettingsObserver(mHandler); - mSettingsObserver.observe(); - - mHandler.sendEmptyMessage(AppLockHandler.MSG_READ_STATE); } @Override @@ -219,42 +212,57 @@ public void onStart() { publishLocalService(AppLockService.class, this); } + @Override + public void onBootPhase(int phase) { + if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) { + Slog.v(TAG_APPLOCK, "onBootPhase PHASE_SYSTEM_SERVICES_READY"); + mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE); + mPackageManager = mContext.getPackageManager(); + mIpm = AppGlobals.getPackageManager(); + } + } + @Override public void onUnlockUser(int userHandle) { - if (DEBUG_APPLOCK) Slog.v(TAG_APPLOCK, "onUnlockUser()"); - mUserId = userHandle; - mPackageManager = mContext.getPackageManager(); - mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE); - mHandler.sendEmptyMessage(AppLockHandler.MSG_INIT_APPS); + if (DEBUG_APPLOCK) Slog.v(TAG_APPLOCK, "onUnlockUser() mUserId:" + userHandle); + if (!UserManager.get(mContext).isManagedProfile(userHandle)) { + if (DEBUG_APPLOCK) Slog.v(TAG_APPLOCK, "onUnlockUser() is NOT ManagedProfile"); + mUserId = userHandle; + mHandler.sendEmptyMessage(AppLockHandler.MSG_INIT_APPS); + } } @Override public void onSwitchUser(int userHandle) { - if (DEBUG_APPLOCK) Slog.v(TAG_APPLOCK, "onSwitchUser()"); - mUserId = userHandle; - mHandler.sendEmptyMessage(AppLockHandler.MSG_INIT_APPS); + if (DEBUG_APPLOCK) Slog.v(TAG_APPLOCK, "onSwitchUser() mUserId:" + userHandle); + if (!UserManager.get(mContext).isManagedProfile(userHandle)) { + if (DEBUG_APPLOCK) Slog.v(TAG_APPLOCK, "onSwitchUser() is NOT ManagedProfile"); + mUserId = userHandle; + mHandler.sendEmptyMessage(AppLockHandler.MSG_INIT_APPS); + } } @Override public void onStopUser(int userHandle) { - if (DEBUG_APPLOCK) Slog.v(TAG_APPLOCK, "onStopUser()"); - mEnabled.set(false); + if (DEBUG_APPLOCK) Slog.v(TAG_APPLOCK, "onStopUser() userHandle:" + userHandle); + if (mUserId == userHandle) { + mUserId = ActivityManager.getCurrentUser(); + mHandler.sendEmptyMessage(AppLockHandler.MSG_INIT_APPS); + } } private void initLockedApps() { if (DEBUG_APPLOCK) Slog.v(TAG_APPLOCK, "initLockedApps(" + mUserId + ")"); mUserHandle = new UserHandle(mUserId); - if (mUserManager.isManagedProfile(mUserId)) { - if (DEBUG_APPLOCK) Slog.v(TAG_APPLOCK, "Disabled"); - mEnabled.set(false); - } else { - mFile = new AtomicFile(getFile()); - if (DEBUG_APPLOCK) Slog.v(TAG_APPLOCK, "Enabled"); - mEnabled.set(true); - readState(); - clearOpenedAppsList(); - } mIsSecure = isSecure(); + mFile = new AtomicFile(getFile()); + readState(); + clearOpenedAppsList(); + + mShowOnlyOnWake = Settings.System.getIntForUser(mContext + .getContentResolver(), + Settings.System.APP_LOCK_SHOW_ONLY_ON_WAKE, 0, + mUserId) != 0; } private File getFile() { @@ -265,9 +273,7 @@ private File getFile() { private void readState() { if (DEBUG_APPLOCK) Slog.v(TAG_APPLOCK, "readState()"); - if (!mEnabled.get()) { - return; - } + mAppsList.clear(); try (FileInputStream in = mFile.openRead()) { XmlPullParser parser = Xml.newPullParser(); parser.setInput(in, null); @@ -301,7 +307,6 @@ private void parseXml(XmlPullParser parser) throws IOException, private void parsePackages(XmlPullParser parser) throws IOException, XmlPullParserException { - mAppsList.clear(); int type; int depth; int innerDepth = parser.getDepth() + 1; @@ -314,12 +319,14 @@ private void parsePackages(XmlPullParser parser) throws IOException, if (parser.getName().equals(TAG_PACKAGE)) { String pkgName = parser.getAttributeValue(null, ATTRIBUTE_NAME); String appOpMode = parser.getAttributeValue(null, ATTRIBUTE_OP_MODE); + String notifHide = parser.getAttributeValue(null, ATTRIBUTE_NOTIFICATION); AppLockContainer cont = new AppLockContainer(pkgName, (appOpMode == null) - ? -1 : Integer.parseInt(appOpMode)); + ? -1 : Integer.parseInt(appOpMode), (notifHide == null) ? false + : Boolean.parseBoolean(notifHide)); writeAfter = (appOpMode == null) || (Integer.parseInt(appOpMode) == -1); mAppsList.put(pkgName, cont); if (DEBUG_APPLOCK) Slog.v(TAG_APPLOCK, "parsePackages(): pkgName=" + pkgName - + " appOpMode=" + appOpMode); + + " appOpMode=" + appOpMode + " notifHide:" + notifHide); } } if (writeAfter) { @@ -330,9 +337,6 @@ private void parsePackages(XmlPullParser parser) throws IOException, private void writeState() { if (DEBUG_APPLOCK) Slog.v(TAG_APPLOCK, "writeState()"); - if (!mEnabled.get()) { - return; - } FileOutputStream out = null; try { @@ -358,39 +362,53 @@ private void writeState() { private void serializeLockedApps(XmlSerializer serializer) throws IOException { serializer.startTag(null, TAG_LOCKED_APPS); - for (int i = 0; i < mAppsList.size(); ++i) { - AppLockContainer cont = mAppsList.valueAt(i); + ArrayList apps = new ArrayList<>(mAppsList.values()); + for (AppLockContainer app : apps) { serializer.startTag(null, TAG_PACKAGE); - serializer.attribute(null, ATTRIBUTE_NAME, cont.packageName); - serializer.attribute(null, ATTRIBUTE_OP_MODE, String.valueOf(cont.appOpMode)); + serializer.attribute(null, ATTRIBUTE_NAME, app.packageName); + serializer.attribute(null, ATTRIBUTE_OP_MODE, String.valueOf(app.appOpMode)); + serializer.attribute(null, ATTRIBUTE_NOTIFICATION, String.valueOf(app.notifHide)); serializer.endTag(null, TAG_PACKAGE); } serializer.endTag(null, TAG_LOCKED_APPS); } private void addAppToList(String packageName) { - if (!mEnabled.get()) { - return; - } if (DEBUG_APPLOCK) Slog.v(TAG, "addAppToList packageName:" + packageName); if (!mAppsList.containsKey(packageName)) { - AppLockContainer cont = new AppLockContainer(packageName, -1); + AppLockContainer cont = new AppLockContainer(packageName, -1, false); mAppsList.put(packageName, cont); mHandler.sendEmptyMessage(AppLockHandler.MSG_WRITE_STATE); - dispatchCallbacks(packageName, false); + dispatchCallbacks(packageName); } } private void removeAppFromList(String packageName) { - if (!mEnabled.get()) { - return; - } if (mAppsList.containsKey(packageName)) { AppLockContainer cont = getAppLockContainer(packageName); cont.appRemovedFromList(); mAppsList.remove(packageName); mHandler.sendEmptyMessage(AppLockHandler.MSG_WRITE_STATE); - dispatchCallbacks(packageName, true); + dispatchCallbacks(packageName); + } + } + + private boolean getAppNotificationHide(String packageName) { + AppLockContainer cont = getAppLockContainer(packageName); + if (cont != null) { + return cont.notifHide; + } + return false; + } + + private void setAppNotificationHide(String packageName, boolean hide) { + AppLockContainer cont = getAppLockContainer(packageName); + if (cont != null) { + if (cont.notifHide != hide) { + cont.notifHide = hide; + mHandler.sendEmptyMessage(AppLockHandler.MSG_WRITE_STATE); + dispatchCallbacks(packageName); + } } } @@ -398,27 +416,27 @@ public void reportPasswordChanged(int userId) { if (mUserId == userId) { mIsSecure = isSecure(); if (DEBUG_APPLOCK) Slog.v(TAG_APPLOCK, "reportPasswordChanged() mIsSecure:" + mIsSecure); + for (AppLockContainer app : mAppsList.values()) { + app.reportPasswordChanged(); + } } } public boolean isAppLocked(String packageName) { - if (!mEnabled.get() || !mIsSecure) { + if (!mIsSecure) { return false; } return mAppsList.containsKey(packageName); } private AppLockContainer getAppLockContainer(String packageName) { - if (!mEnabled.get()) { - return null; - } return mAppsList.get(packageName); } private void clearOpenedAppsList() { if (DEBUG_APPLOCK) Slog.v(TAG_APPLOCK, "clearOpenedAppsList()"); for (String p : mOpenedApplicationsIndex) { - dispatchCallbacks(p, false); + dispatchCallbacks(p); } mOpenedApplicationsIndex.clear(); } @@ -436,22 +454,24 @@ public boolean isAlarmOrCallIntent(Intent intent) { if (DEBUG_APPLOCK) Slog.v(TAG_APPLOCK, "isAlarmOrCallIntent() intent:" + intent); String intentClassName = intent.getComponent().getClassName().toLowerCase(); - return (intentClassName.contains("call") || intentClassName.contains("voip") - || intentClassName.contains("alarm")) - && intentClassName.contains("activity"); + return intentClassName.contains("callactivity") + || intentClassName.contains("callingactivity") + || intentClassName.contains("voipactivity") + || intentClassName.contains("alarmactivity"); } void removeOpenedApp(String packageName) { - if (isAppOpen(packageName)) { - if (DEBUG_APPLOCK) Slog.v(TAG_APPLOCK, "removeOpenedApp(" + packageName + ")"); - mOpenedApplicationsIndex.remove(packageName); - dispatchCallbacks(packageName, false); + if (DEBUG_APPLOCK) Slog.v(TAG_APPLOCK, "removeOpenedApp(" + packageName + ")"); + if (mOpenedApplicationsIndex.remove(packageName)) { + dispatchCallbacks(packageName); } } void addOpenedApp(String packageName) { if (DEBUG_APPLOCK) Slog.v(TAG_APPLOCK, "addOpenedApp(" + packageName + ")"); - mOpenedApplicationsIndex.add(packageName); + if (mOpenedApplicationsIndex.add(packageName)) { + dispatchCallbacks(packageName); + } } public void launchBeforeActivity(String packageName) { @@ -537,7 +557,7 @@ public void setKeyguardShown(boolean shown) { if (mCurrent != null) { launchBeforeActivity(mCurrent.packageName); } - }, 150); + }, 200); } } @@ -572,7 +592,7 @@ private int getLockedAppsCount() { return mIsSecure ? mAppsList.size() : 0; } - private void dispatchCallbacks(String packageName, boolean opened) { + private void dispatchCallbacks(String packageName) { mHandler.post(() -> { synchronized (mCallbacks) { final int N = mCallbacks.size(); @@ -581,7 +601,7 @@ private void dispatchCallbacks(String packageName, boolean opened) { final IAppLockCallback callback = mCallbacks.valueAt(i); try { if (callback != null) { - callback.onAppStateChanged(packageName, opened); + callback.onAppStateChanged(packageName); } else { cleanup = true; } @@ -629,31 +649,17 @@ private void removeAppLockCallback(IAppLockCallback callback) { }); } - private class SettingsObserver extends ContentObserver { - - SettingsObserver(Handler handler) { - super(handler); - } - - void observe() { - ContentResolver resolver = mContext.getContentResolver(); - resolver.registerContentObserver(Settings.System.getUriFor( - Settings.System.APP_LOCK_SHOW_ONLY_ON_WAKE), false, this, - UserHandle.USER_ALL); - mShowOnlyOnWake = Settings.System.getIntForUser(mContext - .getContentResolver(), - Settings.System.APP_LOCK_SHOW_ONLY_ON_WAKE, 0, - mUserId) != 0; - } + private void setShowOnlyOnWake(boolean showOnce) { + mShowOnlyOnWake = showOnce; + Settings.System.putIntForUser(mContext + .getContentResolver(), + Settings.System.APP_LOCK_SHOW_ONLY_ON_WAKE, + showOnce ? 1 : 0, + mUserId); + } - @Override - public void onChange(boolean selfChange) { - mShowOnlyOnWake = Settings.System.getIntForUser(mContext - .getContentResolver(), - Settings.System.APP_LOCK_SHOW_ONLY_ON_WAKE, 0, - mUserId) != 0; - clearOpenedAppsList(); - } + private boolean getShowOnlyOnWake() { + return mShowOnlyOnWake; } private class AppLockImpl extends IAppLockService.Stub { @@ -677,6 +683,16 @@ public boolean isAppOpen(String packageName) { return AppLockService.this.isAppOpen(packageName); } + @Override + public void setShowOnlyOnWake(boolean showOnce) { + AppLockService.this.setShowOnlyOnWake(showOnce); + } + + @Override + public boolean getShowOnlyOnWake() { + return AppLockService.this.getShowOnlyOnWake(); + } + @Override public int getLockedAppsCount() { return AppLockService.this.getLockedAppsCount(); @@ -687,6 +703,16 @@ public List getLockedPackages() { return AppLockService.this.getLockedPackages(); } + @Override + public boolean getAppNotificationHide(String packageName) { + return AppLockService.this.getAppNotificationHide(packageName); + } + + @Override + public void setAppNotificationHide(String packageName, boolean hide) { + AppLockService.this.setAppNotificationHide(packageName, hide); + } + @Override public void addAppLockCallback(IAppLockCallback callback) { AppLockService.this.addAppLockCallback(callback); @@ -715,9 +741,6 @@ public void handleMessage(android.os.Message msg) { case MSG_INIT_APPS: initLockedApps(); break; - case MSG_READ_STATE: - readState(); - break; case MSG_WRITE_STATE: writeState(); break; @@ -738,9 +761,22 @@ private class AppLockContainer { private CharSequence appLabel; private int appOpMode = -1; private Intent intent; + private boolean notifHide; - public AppLockContainer(String pkg, int opMode) { + public AppLockContainer(String pkg, int opMode, boolean hideNotif) { packageName = pkg; + notifHide = hideNotif; + + final long ident = Binder.clearCallingIdentity(); + try { + mIpm.setBlockUninstallForUser(packageName, mIsSecure, mUserId); + } catch (RemoteException re) { + // Shouldn't happen. + Slog.e(TAG, "Failed to setBlockUninstallForUser", re); + } finally { + Binder.restoreCallingIdentity(ident); + } + try { aInfo = mPackageManager.getApplicationInfo(packageName, 0); } catch(PackageManager.NameNotFoundException e) { @@ -792,13 +828,35 @@ private void startActivityAfterUnlock() { private void onUnlockSucceed() { addOpenedApp(packageName); startActivityAfterUnlock(); - dispatchCallbacks(packageName, true); } private void appRemovedFromList() { Slog.d(TAG, "appRemovedFromList() appOpMode: " + appOpMode); + + final long ident = Binder.clearCallingIdentity(); + try { + mIpm.setBlockUninstallForUser(packageName, false, mUserId); + } catch (RemoteException re) { + // Shouldn't happen. + Slog.e(TAG, "Failed to setBlockUninstallForUser", re); + } finally { + Binder.restoreCallingIdentity(ident); + } + mAppOpsManager.setMode(AppOpsManager.OP_SYSTEM_ALERT_WINDOW, aInfo.uid, packageName, appOpMode); } + + private void reportPasswordChanged() { + final long ident = Binder.clearCallingIdentity(); + try { + mIpm.setBlockUninstallForUser(packageName, mIsSecure, mUserId); + } catch (RemoteException re) { + // Shouldn't happen. + Slog.e(TAG, "Failed to setBlockUninstallForUser", re); + } finally { + Binder.restoreCallingIdentity(ident); + } + } } } From 36451daab559e4d6f6d1b8799db6624b4b25e80b Mon Sep 17 00:00:00 2001 From: ezio84 Date: Sun, 3 Sep 2017 16:45:40 +0200 Subject: [PATCH 18/26] Allow longpress power button to toggle torch [1/2] * Adapt to AOSPA by @brituj * Thanks beanstown106 and lineage guys for the initial longpress action bringup in PhoneWindowManager * Allow torch action also on ambient display Change-Id: I12da044f86c7b625872607529cf8524615cf576b Signed-off-by: Anirudh Gupta --- core/java/android/provider/Settings.java | 17 +++ .../server/policy/PhoneWindowManager.java | 133 +++++++++++++++++- 2 files changed, 145 insertions(+), 5 deletions(-) diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 17f9c8c8860..83869dc2c64 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -8586,6 +8586,20 @@ public boolean validate(@Nullable String value) { private static final Validator CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED_VALIDATOR = BOOLEAN_VALIDATOR; + /** + * Whether the torch launch gesture to 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"; + + private static final Validator TORCH_POWER_BUTTON_GESTURE_VALIDATOR = + NON_NEGATIVE_INTEGER_VALIDATOR; + /** * Whether the camera double twist gesture to flip between front and back mode should be * enabled. @@ -9374,6 +9388,7 @@ public boolean validate(@Nullable String value) { SYNC_PARENT_SOUNDS, CAMERA_DOUBLE_TWIST_TO_FLIP_ENABLED, CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED, + TORCH_POWER_BUTTON_GESTURE, SYSTEM_NAVIGATION_KEYS_ENABLED, QS_TILES, DOZE_ENABLED, @@ -9544,6 +9559,8 @@ public boolean validate(@Nullable String value) { CAMERA_DOUBLE_TWIST_TO_FLIP_ENABLED_VALIDATOR); VALIDATORS.put(CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED, CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED_VALIDATOR); + VALIDATORS.put(TORCH_POWER_BUTTON_GESTURE, + TORCH_POWER_BUTTON_GESTURE_VALIDATOR); VALIDATORS.put(SYSTEM_NAVIGATION_KEYS_ENABLED, SYSTEM_NAVIGATION_KEYS_ENABLED_VALIDATOR); VALIDATORS.put(QS_TILES, QS_TILES_VALIDATOR); diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 4561218cc2b..cb6cdf1d73f 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -130,6 +130,10 @@ import android.database.ContentObserver; import android.graphics.Rect; import android.graphics.drawable.Drawable; +import android.hardware.camera2.CameraAccessException; +import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CameraManager; +import android.hardware.camera2.CameraManager.TorchCallback; import android.hardware.display.DisplayManager; import android.hardware.hdmi.HdmiAudioSystemClient; import android.hardware.hdmi.HdmiControlManager; @@ -170,6 +174,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.LongSparseArray; import android.util.MutableBoolean; @@ -282,6 +287,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_TORCH = 10; // must match: config_veryLongPresOnPowerBehavior in config.xml static final int VERY_LONG_PRESS_POWER_NOTHING = 0; @@ -564,6 +570,9 @@ public void onDrawn() { // Whether to support long press from power button in non-interactive mode private boolean mSupportLongPressPowerWhenNonInteractive; + // Power long press action saved on key down that should happen on key up + private int mResolvedLongPressOnPowerBehavior; + // Whether to go to sleep entering theater mode from power button private boolean mGoToSleepOnButtonPressTheaterMode; @@ -633,6 +642,34 @@ public void onDrawn() { private final List mDeviceKeyHandlers = new ArrayList<>(); + private CameraManager mCameraManager; + private String mCameraId; + private int mTorchActionMode; + private boolean mTorchEnabled = false; + private TorchCallback mTorchCallback = new TorchCallback() { + @Override + public void onTorchModeChanged(String cameraId, boolean enabled) { + if (!TextUtils.isEmpty(mCameraId)) { + if (mCameraId.equals(cameraId)) { + mTorchEnabled = enabled; + } + } else { + mTorchEnabled = enabled; + } + } + + @Override + public void onTorchModeUnavailable(String cameraId) { + if (!TextUtils.isEmpty(mCameraId)) { + if (mCameraId.equals(cameraId)) { + mTorchEnabled = false; + } + } else { + mTorchEnabled = false; + } + } + }; + 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; @@ -658,6 +695,7 @@ public void onDrawn() { private static final int MSG_NOTIFY_USER_ACTIVITY = 26; private static final int MSG_RINGER_TOGGLE_CHORD = 27; private static final int MSG_MOVE_DISPLAY_TO_TOP = 28; + private static final int MSG_TOGGLE_TORCH = 50; // Global actions on lockk screen private boolean mGlobalActionsOnLockDisable; @@ -756,6 +794,10 @@ public void handleMessage(Message msg) { mWindowManagerFuncs.moveDisplayToTop(msg.arg1); mMovingDisplayToTopKeyTriggered = false; break; + case MSG_TOGGLE_TORCH: + performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, true, "Toggle Torch"); + toggleFlashLight(); + break; } } } @@ -821,6 +863,9 @@ void observe() { Settings.System.LOCK_POWER_MENU_DISABLED), false, this); resolver.registerContentObserver(Settings.System.getUriFor( Settings.System.SWIPE_TO_SCREENSHOT), false, this); + resolver.registerContentObserver(Settings.Secure.getUriFor( + Settings.Secure.TORCH_POWER_BUTTON_GESTURE), false, this, + UserHandle.USER_ALL); updateSettings(); } @@ -1016,6 +1061,7 @@ private void interceptPowerKeyDown(KeyEvent event, boolean interactive) { // When interactive, we're already awake. // Wait for a long press or for the button to be released to decide what to do. if (hasLongPressOnPowerBehavior()) { + mResolvedLongPressOnPowerBehavior = getResolvedLongPressOnPowerBehavior(); if ((event.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0) { powerLongPress(); } else { @@ -1032,9 +1078,12 @@ private void interceptPowerKeyDown(KeyEvent event, boolean interactive) { } } } else { - wakeUpFromPowerKey(event.getDownTime()); - - if (mSupportLongPressPowerWhenNonInteractive && hasLongPressOnPowerBehavior()) { + if ((mTorchActionMode == 1) || (mSupportLongPressPowerWhenNonInteractive + && hasLongPressOnPowerBehavior())) { + mResolvedLongPressOnPowerBehavior = getResolvedLongPressOnPowerBehavior(); + if (mResolvedLongPressOnPowerBehavior != LONG_PRESS_POWER_TORCH) { + wakeUpFromPowerKey(event.getDownTime()); + } if ((event.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0) { powerLongPress(); } else { @@ -1052,6 +1101,7 @@ private void interceptPowerKeyDown(KeyEvent event, boolean interactive) { mBeganFromNonInteractive = true; } else { + wakeUpFromPowerKey(event.getDownTime()); final int maxCount = getMaxMultiPressPowerCount(); if (maxCount <= 1) { @@ -1064,6 +1114,18 @@ private void interceptPowerKeyDown(KeyEvent event, boolean interactive) { } } + private boolean isDozeMode() { + IDreamManager dreamManager = getDreamManager(); + try { + if (dreamManager != null && dreamManager.isDreaming()) { + return true; + } + } catch (RemoteException e) { + return false; + } + return false; + } + private void interceptPowerKeyUp(KeyEvent event, boolean interactive, boolean canceled) { final boolean handled = canceled || mPowerKeyHandled; mScreenshotChordPowerKeyTriggered = false; @@ -1111,6 +1173,11 @@ private void cancelPendingPowerKeyAction() { if (!mPowerKeyHandled) { mPowerKeyHandled = true; mHandler.removeMessages(MSG_POWER_LONG_PRESS); + // See if we deferred screen wake because long press power for torch is enabled + if (mResolvedLongPressOnPowerBehavior == LONG_PRESS_POWER_TORCH + && (!isScreenOn() || isDozeMode())) { + wakeUpFromPowerKey(SystemClock.uptimeMillis()); + } } if (hasVeryLongPressOnPowerBehavior()) { mHandler.removeMessages(MSG_POWER_VERY_LONG_PRESS); @@ -1126,7 +1193,7 @@ private void cancelPendingBackKeyAction() { } private void powerPress(long eventTime, boolean interactive, int count) { - if (mDefaultDisplayPolicy.isScreenOnEarly() && !mDefaultDisplayPolicy.isScreenOnFully()) { + if (!isDozeMode() && mDefaultDisplayPolicy.isScreenOnEarly() && !mDefaultDisplayPolicy.isScreenOnFully()) { Slog.i(TAG, "Suppressed redundant power key press while " + "already in the process of turning the screen on."); return; @@ -1173,6 +1240,8 @@ private void powerPress(long eventTime, boolean interactive, int count) { break; } } + } else if ((mTorchActionMode == 1) && (!isScreenOn() || isDozeMode())) { + wakeUpFromPowerKey(eventTime); } } @@ -1257,6 +1326,32 @@ private void powerMultiPressAction(long eventTime, boolean interactive, int beha } } + private String getCameraId() throws CameraAccessException { + String[] ids = mCameraManager.getCameraIdList(); + if (ids != null && ids.length > 0) { + for (String id : ids) { + CameraCharacteristics c = mCameraManager.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 id; + } + } + } + return ""; + } + + private void toggleFlashLight() { + try { + mCameraManager.setTorchMode(mCameraId, !mTorchEnabled); + } catch (CameraAccessException e) { + + } catch (IllegalArgumentException e) { + + } + } + private int getLidBehavior() { return Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.LID_BEHAVIOR, LID_BEHAVIOR_NONE); @@ -1273,7 +1368,7 @@ private int getMaxMultiPressPowerCount() { } private void powerLongPress() { - final int behavior = getResolvedLongPressOnPowerBehavior(); + final int behavior = mResolvedLongPressOnPowerBehavior; switch (behavior) { case LONG_PRESS_POWER_NOTHING: break; @@ -1305,6 +1400,15 @@ private void powerLongPress() { final int powerKeyDeviceId = Integer.MIN_VALUE; launchAssistAction(null, powerKeyDeviceId); break; + case LONG_PRESS_POWER_TORCH: + mPowerKeyHandled = true; + // Toggle torch state asynchronously to help protect against + // a misbehaving cameraservice from blocking systemui. + mHandler.removeMessages(MSG_TOGGLE_TORCH); + Message msg = mHandler.obtainMessage(MSG_TOGGLE_TORCH); + msg.setAsynchronous(true); + msg.sendToTarget(); + break; } } @@ -1356,6 +1460,9 @@ private int getResolvedLongPressOnPowerBehavior() { if (FactoryTest.isLongPressOnPowerOffEnabled()) { return LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM; } + if ((mTorchActionMode == 1) && (!isScreenOn() || isDozeMode())) { + return LONG_PRESS_POWER_TORCH; + } return mLongPressOnPowerBehavior; } @@ -1813,6 +1920,7 @@ public void init(Context context, IWindowManager windowManager, mHasFeatureLeanback = mContext.getPackageManager().hasSystemFeature(FEATURE_LEANBACK); mHasFeatureAuto = mContext.getPackageManager().hasSystemFeature(FEATURE_AUTOMOTIVE); mHasFeatureHdmiCec = mContext.getPackageManager().hasSystemFeature(FEATURE_HDMI_CEC); + mCameraManager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE); mAccessibilityShortcutController = new AccessibilityShortcutController(mContext, new Handler(), mCurrentUserId); mLogger = new MetricsLogger(); @@ -2163,6 +2271,9 @@ public void updateSettings() { com.android.internal.R.integer.config_veryLongPressOnPowerBehavior)); mGlobalActionsOnLockDisable = Settings.System.getInt(resolver, Settings.System.LOCK_POWER_MENU_DISABLED, 0) != 0; + mTorchActionMode = Settings.Secure.getIntForUser(resolver, + Settings.Secure.TORCH_POWER_BUTTON_GESTURE, 0, + UserHandle.USER_CURRENT); } if (updateRotation) { updateRotation(true); @@ -4991,6 +5102,18 @@ public void systemReady() { mVrManagerInternal.addPersistentVrModeStateListener(mPersistentVrModeListener); } + mCameraId = ""; + + try { + mCameraId = getCameraId(); + } catch (Throwable e) { + Log.e(TAG, "Couldn't initialize.", e); + } + + if (mCameraManager != null) { + mCameraManager.registerTorchCallback(mTorchCallback, mHandler); + } + readCameraLensCoverState(); updateUiMode(); mDefaultDisplayRotation.updateOrientationListener(); From 71ea7424e679cb0e9e92578031a4e5b0ad099cb4 Mon Sep 17 00:00:00 2001 From: Sam Mortimer Date: Thu, 23 Jul 2020 19:22:01 +0000 Subject: [PATCH 19/26] fw/b torch: Let long press power turn torch off when screen is on. * When the torch is on, any subsequent long press power is almost certainly intended to turn the torch off (regardless of screen state). Therefore, always allow long press power to toggle torch if the torch is on. * Tested: long press power toggles torch on/off with screen off. long press power toggles torch off with screen on and torch on. long press power brings up global actions menu with screen on and torch off. Change-Id: I932caa9f3be06d14408aea2ecb3a6eca73e052e0 Signed-off-by: Mesquita --- .../core/java/com/android/server/policy/PhoneWindowManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index cb6cdf1d73f..5b23bf1324f 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -1460,7 +1460,7 @@ private int getResolvedLongPressOnPowerBehavior() { if (FactoryTest.isLongPressOnPowerOffEnabled()) { return LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM; } - if ((mTorchActionMode == 1) && (!isScreenOn() || isDozeMode())) { + if ((mTorchActionMode == 1) && (!isScreenOn() || isDozeMode() || mTorchEnabled)) { return LONG_PRESS_POWER_TORCH; } return mLongPressOnPowerBehavior; From 8ec8e92ae196769af5a0987399e5afba983f8b0b Mon Sep 17 00:00:00 2001 From: Lucchetto Date: Thu, 28 Mar 2019 18:41:03 +0100 Subject: [PATCH 20/26] base: Smart Charging (1/4) Change-Id: Ifd9e1d8094287e145870510b2f9e85829c211603 --- core/java/android/provider/Settings.java | 11 ++++ core/res/res/values/reloaded_config.xml | 2 + core/res/res/values/reloaded_symbols.xml | 2 + .../server/power/PowerManagerService.java | 66 +++++++++++++++++++ 4 files changed, 81 insertions(+) diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 83869dc2c64..edb44697867 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -4807,6 +4807,17 @@ public boolean validate(@Nullable String value) { GESTURE_PILL_TOGGLE, }; + /** + * @hide + */ + public static final String SMART_CHARGING = "smart_charging"; + + /** + * @hide + */ + public static final String SMART_CHARGING_LEVEL = "smart_charging_level"; + + /** * Keys we no longer back up under the current schema, but want to continue to * process when restoring historical backup datasets. diff --git a/core/res/res/values/reloaded_config.xml b/core/res/res/values/reloaded_config.xml index 0a2ac21e6fc..7b1f08e7f26 100644 --- a/core/res/res/values/reloaded_config.xml +++ b/core/res/res/values/reloaded_config.xml @@ -47,4 +47,6 @@ true + + 80 diff --git a/core/res/res/values/reloaded_symbols.xml b/core/res/res/values/reloaded_symbols.xml index 56c0713838d..fdc1983aabd 100644 --- a/core/res/res/values/reloaded_symbols.xml +++ b/core/res/res/values/reloaded_symbols.xml @@ -48,4 +48,6 @@ + + diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index 35e0897f5ae..fbafa793490 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -45,6 +45,7 @@ import android.os.BatteryManagerInternal; import android.os.BatterySaverPolicyConfig; import android.os.Binder; +import android.os.FileUtils; import android.os.Handler; import android.os.IBinder; import android.os.IPowerManager; @@ -102,6 +103,7 @@ import com.android.server.power.batterysaver.BatterySavingStats; import java.io.FileDescriptor; +import java.io.IOException; import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -223,6 +225,11 @@ public final class PowerManagerService extends SystemService // property for last reboot reason private static final String REBOOT_PROPERTY = "sys.boot.reason"; + // Smart charging: sysfs node of charger + private static final String BATTERY_CHARGER_PATH = + "/sys/class/power_supply/battery/battery_charging_enabled"; + private static final String CHARGER_PATH = "/sys/class/power_supply/battery/charging_enabled"; + private final Context mContext; private final ServiceThread mHandlerThread; private final PowerManagerHandler mHandler; @@ -588,6 +595,16 @@ public ProfilePowerState(@UserIdInt int userId, long screenOffTimeout) { } } + // Smart charging + private boolean mSmartChargingEnabled; + private int mSmartChargingLevel; + private int mSmartChargingLevelDefaultConfig; + // Handle charger + private boolean mUseCharger = true; + // Handle battery charging, when false the charger will keep the + // battery at the current level + private boolean mChargeBattery = true; + /** * All times are in milliseconds. These constants are kept synchronized with the system * global Settings. Any access to this class or its fields should be done while @@ -919,6 +936,13 @@ public void systemReady(IAppOpsService appOps) { resolver.registerContentObserver(Settings.Global.getUriFor( Settings.Global.DEVICE_DEMO_MODE), false, mSettingsObserver, UserHandle.USER_SYSTEM); + resolver.registerContentObserver(Settings.System.getUriFor( + Settings.System.SMART_CHARGING), + false, mSettingsObserver, UserHandle.USER_ALL); + resolver.registerContentObserver(Settings.System.getUriFor( + Settings.System.SMART_CHARGING_LEVEL), + false, mSettingsObserver, UserHandle.USER_ALL); + IVrManager vrManager = IVrManager.Stub.asInterface(getBinderService(Context.VR_SERVICE)); if (vrManager != null) { try { @@ -990,6 +1014,8 @@ void readConfigurationLocked() { com.android.internal.R.fraction.config_maximumScreenDimRatio, 1, 1); mSupportsDoubleTapWakeConfig = resources.getBoolean( com.android.internal.R.bool.config_supportDoubleTapWake); + mSmartChargingLevelDefaultConfig = resources.getInteger( + com.android.internal.R.integer.config_smartChargingBatteryLevel); } private void updateSettingsLocked() { @@ -1018,6 +1044,11 @@ private void updateSettingsLocked() { mTheaterModeEnabled = Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.THEATER_MODE_ON, 0) == 1; mAlwaysOnEnabled = mAmbientDisplayConfiguration.alwaysOnEnabled(UserHandle.USER_CURRENT); + mSmartChargingEnabled = Settings.System.getInt(resolver, + Settings.System.SMART_CHARGING, 0) == 1; + mSmartChargingLevel = Settings.System.getInt(resolver, + Settings.System.SMART_CHARGING_LEVEL, + mSmartChargingLevelDefaultConfig); if (mSupportsDoubleTapWakeConfig) { boolean doubleTapWakeEnabled = Settings.Secure.getIntForUser(resolver, @@ -1045,6 +1076,7 @@ private void updateSettingsLocked() { private void handleSettingsChangedLocked() { updateSettingsLocked(); updatePowerStateLocked(); + updateSmartChargingStatus(); } private void acquireWakeLockInternal(IBinder lock, int flags, String tag, String packageName, @@ -1804,6 +1836,40 @@ private void updateIsPoweredLocked(int dirty) { } mBatterySaverStateMachine.setBatteryStatus(mIsPowered, mBatteryLevel, mBatteryLevelLow); + updateSmartChargingStatus(); + } + } + + private void updateSmartChargingStatus() { + if (mIsPowered || (mUseCharger == false)) { + boolean allowBatteryCharging = true; + boolean allowCharger = true; + if (mSmartChargingEnabled && (mBatteryLevel >= mSmartChargingLevel)) { + if (mBatteryLevel > mSmartChargingLevel) { + allowCharger = false; + } + allowBatteryCharging = false; + } + + if (mChargeBattery != allowBatteryCharging) { + try { + mChargeBattery = allowBatteryCharging; + FileUtils.stringToFile(BATTERY_CHARGER_PATH, mChargeBattery ? "1" : "0"); + } catch (IOException e) { + Slog.e(TAG, "failed to write to " + BATTERY_CHARGER_PATH); + mChargeBattery = !mChargeBattery; + } + } + + if (mUseCharger != allowCharger) { + try { + mUseCharger = allowCharger; + FileUtils.stringToFile(CHARGER_PATH, mUseCharger ? "1" : "0"); + } catch (IOException e) { + Slog.e(TAG, "failed to write to " + CHARGER_PATH); + mUseCharger = !mUseCharger; + } + } } } From ce41842a944487a2acdcfa2d279263f3fe7d0419 Mon Sep 17 00:00:00 2001 From: DennySPB Date: Fri, 17 May 2019 09:49:23 +0300 Subject: [PATCH 21/26] Smart Charging: rework for using more common sysfs node [1/2] /sys/class/power_supply/battery/input_suspend Change-Id: Id7f90927bc038349c46154c028f191a3a22e4793 Signed-off-by: DennySPb --- .../server/power/PowerManagerService.java | 52 +++++++------------ 1 file changed, 18 insertions(+), 34 deletions(-) diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index fbafa793490..d528f960bdc 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -226,9 +226,8 @@ public final class PowerManagerService extends SystemService private static final String REBOOT_PROPERTY = "sys.boot.reason"; // Smart charging: sysfs node of charger - private static final String BATTERY_CHARGER_PATH = - "/sys/class/power_supply/battery/battery_charging_enabled"; - private static final String CHARGER_PATH = "/sys/class/power_supply/battery/charging_enabled"; + private static final String POWER_INTPUT_SUSPEND_NODE = + "/sys/class/power_supply/battery/input_suspend"; private final Context mContext; private final ServiceThread mHandlerThread; @@ -597,13 +596,9 @@ public ProfilePowerState(@UserIdInt int userId, long screenOffTimeout) { // Smart charging private boolean mSmartChargingEnabled; + private boolean mPowerInputSuspended = false; private int mSmartChargingLevel; private int mSmartChargingLevelDefaultConfig; - // Handle charger - private boolean mUseCharger = true; - // Handle battery charging, when false the charger will keep the - // battery at the current level - private boolean mChargeBattery = true; /** * All times are in milliseconds. These constants are kept synchronized with the system @@ -1841,34 +1836,23 @@ private void updateIsPoweredLocked(int dirty) { } private void updateSmartChargingStatus() { - if (mIsPowered || (mUseCharger == false)) { - boolean allowBatteryCharging = true; - boolean allowCharger = true; - if (mSmartChargingEnabled && (mBatteryLevel >= mSmartChargingLevel)) { - if (mBatteryLevel > mSmartChargingLevel) { - allowCharger = false; - } - allowBatteryCharging = false; - } - - if (mChargeBattery != allowBatteryCharging) { - try { - mChargeBattery = allowBatteryCharging; - FileUtils.stringToFile(BATTERY_CHARGER_PATH, mChargeBattery ? "1" : "0"); - } catch (IOException e) { - Slog.e(TAG, "failed to write to " + BATTERY_CHARGER_PATH); - mChargeBattery = !mChargeBattery; - } + if (mPowerInputSuspended && (mBatteryLevel < mSmartChargingLevel) || + (mPowerInputSuspended && !mSmartChargingEnabled)) { + try { + FileUtils.stringToFile(POWER_INTPUT_SUSPEND_NODE, "0"); + mPowerInputSuspended = false; + } catch (IOException e) { + Slog.e(TAG, "failed to write to " + POWER_INTPUT_SUSPEND_NODE); } + return; + } - if (mUseCharger != allowCharger) { - try { - mUseCharger = allowCharger; - FileUtils.stringToFile(CHARGER_PATH, mUseCharger ? "1" : "0"); - } catch (IOException e) { - Slog.e(TAG, "failed to write to " + CHARGER_PATH); - mUseCharger = !mUseCharger; - } + if (mSmartChargingEnabled && !mPowerInputSuspended && (mBatteryLevel >= mSmartChargingLevel)) { + try { + FileUtils.stringToFile(POWER_INTPUT_SUSPEND_NODE, "1"); + mPowerInputSuspended = true; + } catch (IOException e) { + Slog.e(TAG, "failed to write to " + POWER_INTPUT_SUSPEND_NODE); } } } From 5d65a0488ccd914a3762e8ecfd204199a12f7836 Mon Sep 17 00:00:00 2001 From: DennySPB Date: Tue, 17 Sep 2019 13:50:35 +0200 Subject: [PATCH 22/26] Smart Charging: allow using device overlays for sysfs node, suspend/resume values cause some devices doesn't have input_suspend node default: /sys/class/power_supply/battery/charging_enabled 0 1 Change-Id: I891da793fb81c3d6c5077496855db2086e9e773f --- core/res/res/values/reloaded_config.xml | 5 ++++- core/res/res/values/reloaded_symbols.xml | 7 ++++-- .../server/power/PowerManagerService.java | 22 ++++++++++++------- 3 files changed, 23 insertions(+), 11 deletions(-) diff --git a/core/res/res/values/reloaded_config.xml b/core/res/res/values/reloaded_config.xml index 7b1f08e7f26..e4f35364fa2 100644 --- a/core/res/res/values/reloaded_config.xml +++ b/core/res/res/values/reloaded_config.xml @@ -47,6 +47,9 @@ true - + 80 + /sys/class/power_supply/battery/charging_enabled + 0 + 1 diff --git a/core/res/res/values/reloaded_symbols.xml b/core/res/res/values/reloaded_symbols.xml index fdc1983aabd..ad42eb4e134 100644 --- a/core/res/res/values/reloaded_symbols.xml +++ b/core/res/res/values/reloaded_symbols.xml @@ -48,6 +48,9 @@ - - + + + + + diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index d528f960bdc..b9b2f89c35a 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -225,10 +225,6 @@ public final class PowerManagerService extends SystemService // property for last reboot reason private static final String REBOOT_PROPERTY = "sys.boot.reason"; - // Smart charging: sysfs node of charger - private static final String POWER_INTPUT_SUSPEND_NODE = - "/sys/class/power_supply/battery/input_suspend"; - private final Context mContext; private final ServiceThread mHandlerThread; private final PowerManagerHandler mHandler; @@ -599,6 +595,9 @@ public ProfilePowerState(@UserIdInt int userId, long screenOffTimeout) { private boolean mPowerInputSuspended = false; private int mSmartChargingLevel; private int mSmartChargingLevelDefaultConfig; + private static String mPowerInputSuspendSysfsNode; + private static String mPowerInputSuspendValue; + private static String mPowerInputResumeValue; /** * All times are in milliseconds. These constants are kept synchronized with the system @@ -1009,8 +1008,15 @@ void readConfigurationLocked() { com.android.internal.R.fraction.config_maximumScreenDimRatio, 1, 1); mSupportsDoubleTapWakeConfig = resources.getBoolean( com.android.internal.R.bool.config_supportDoubleTapWake); + // Smart charging mSmartChargingLevelDefaultConfig = resources.getInteger( com.android.internal.R.integer.config_smartChargingBatteryLevel); + mPowerInputSuspendSysfsNode = resources.getString( + com.android.internal.R.string.config_SmartChargingSysfsNode); + mPowerInputSuspendValue = resources.getString( + com.android.internal.R.string.config_SmartChargingSuspendValue); + mPowerInputResumeValue = resources.getString( + com.android.internal.R.string.config_SmartChargingResumeValue); } private void updateSettingsLocked() { @@ -1839,20 +1845,20 @@ private void updateSmartChargingStatus() { if (mPowerInputSuspended && (mBatteryLevel < mSmartChargingLevel) || (mPowerInputSuspended && !mSmartChargingEnabled)) { try { - FileUtils.stringToFile(POWER_INTPUT_SUSPEND_NODE, "0"); + FileUtils.stringToFile(mPowerInputSuspendSysfsNode, mPowerInputResumeValue); mPowerInputSuspended = false; } catch (IOException e) { - Slog.e(TAG, "failed to write to " + POWER_INTPUT_SUSPEND_NODE); + Slog.e(TAG, "failed to write to " + mPowerInputSuspendSysfsNode); } return; } if (mSmartChargingEnabled && !mPowerInputSuspended && (mBatteryLevel >= mSmartChargingLevel)) { try { - FileUtils.stringToFile(POWER_INTPUT_SUSPEND_NODE, "1"); + FileUtils.stringToFile(mPowerInputSuspendSysfsNode, mPowerInputSuspendValue); mPowerInputSuspended = true; } catch (IOException e) { - Slog.e(TAG, "failed to write to " + POWER_INTPUT_SUSPEND_NODE); + Slog.e(TAG, "failed to write to " + mPowerInputSuspendSysfsNode); } } } From 9a54787fb50607d2fb90e1af4a43ee303703e575 Mon Sep 17 00:00:00 2001 From: DennySPB Date: Tue, 18 Jun 2019 13:24:29 +0300 Subject: [PATCH 23/26] Smart Charging: allow user set resume level [1/2] Change-Id: I48bdac2327c13ea377c1d8b675ea95da2e095f02 --- core/java/android/provider/Settings.java | 5 +++++ core/res/res/values/reloaded_config.xml | 1 + core/res/res/values/reloaded_symbols.xml | 1 + .../android/server/power/PowerManagerService.java | 12 +++++++++++- 4 files changed, 18 insertions(+), 1 deletion(-) diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index edb44697867..719585abe59 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -4817,6 +4817,11 @@ public boolean validate(@Nullable String value) { */ public static final String SMART_CHARGING_LEVEL = "smart_charging_level"; + /** + * @hide + */ + public static final String SMART_CHARGING_RESUME_LEVEL = "smart_charging_resume_level"; + /** * Keys we no longer back up under the current schema, but want to continue to diff --git a/core/res/res/values/reloaded_config.xml b/core/res/res/values/reloaded_config.xml index e4f35364fa2..ae8f931505f 100644 --- a/core/res/res/values/reloaded_config.xml +++ b/core/res/res/values/reloaded_config.xml @@ -49,6 +49,7 @@ 80 + 60 /sys/class/power_supply/battery/charging_enabled 0 1 diff --git a/core/res/res/values/reloaded_symbols.xml b/core/res/res/values/reloaded_symbols.xml index ad42eb4e134..62831e51c7d 100644 --- a/core/res/res/values/reloaded_symbols.xml +++ b/core/res/res/values/reloaded_symbols.xml @@ -50,6 +50,7 @@ + diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index b9b2f89c35a..7c5dbe7224e 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -594,7 +594,9 @@ public ProfilePowerState(@UserIdInt int userId, long screenOffTimeout) { private boolean mSmartChargingEnabled; private boolean mPowerInputSuspended = false; private int mSmartChargingLevel; + private int mSmartChargingResumeLevel; private int mSmartChargingLevelDefaultConfig; + private int mSmartChargingResumeLevelDefaultConfig; private static String mPowerInputSuspendSysfsNode; private static String mPowerInputSuspendValue; private static String mPowerInputResumeValue; @@ -936,6 +938,9 @@ public void systemReady(IAppOpsService appOps) { resolver.registerContentObserver(Settings.System.getUriFor( Settings.System.SMART_CHARGING_LEVEL), false, mSettingsObserver, UserHandle.USER_ALL); + resolver.registerContentObserver(Settings.System.getUriFor( + Settings.System.SMART_CHARGING_RESUME_LEVEL), + false, mSettingsObserver, UserHandle.USER_ALL); IVrManager vrManager = IVrManager.Stub.asInterface(getBinderService(Context.VR_SERVICE)); if (vrManager != null) { @@ -1011,6 +1016,8 @@ void readConfigurationLocked() { // Smart charging mSmartChargingLevelDefaultConfig = resources.getInteger( com.android.internal.R.integer.config_smartChargingBatteryLevel); + mSmartChargingResumeLevelDefaultConfig = resources.getInteger( + com.android.internal.R.integer.config_smartChargingBatteryResumeLevel); mPowerInputSuspendSysfsNode = resources.getString( com.android.internal.R.string.config_SmartChargingSysfsNode); mPowerInputSuspendValue = resources.getString( @@ -1050,6 +1057,9 @@ private void updateSettingsLocked() { mSmartChargingLevel = Settings.System.getInt(resolver, Settings.System.SMART_CHARGING_LEVEL, mSmartChargingLevelDefaultConfig); + mSmartChargingResumeLevel = Settings.System.getInt(resolver, + Settings.System.SMART_CHARGING_RESUME_LEVEL, + mSmartChargingResumeLevelDefaultConfig); if (mSupportsDoubleTapWakeConfig) { boolean doubleTapWakeEnabled = Settings.Secure.getIntForUser(resolver, @@ -1842,7 +1852,7 @@ private void updateIsPoweredLocked(int dirty) { } private void updateSmartChargingStatus() { - if (mPowerInputSuspended && (mBatteryLevel < mSmartChargingLevel) || + if (mPowerInputSuspended && (mBatteryLevel <= mSmartChargingResumeLevel) || (mPowerInputSuspended && !mSmartChargingEnabled)) { try { FileUtils.stringToFile(mPowerInputSuspendSysfsNode, mPowerInputResumeValue); From 1d3b9309ec0d14e73b3d0e1c9e7e02c188ff44ef Mon Sep 17 00:00:00 2001 From: Jorge Ruesga Date: Tue, 24 Nov 2015 23:31:56 +0800 Subject: [PATCH 24/26] frameworks: Reset battery stats [1/2] BenzoEdit: Added permission to priv-app whitelist for Settings Change-Id: I1a1c0831d55c2da01c02da29e78a2c5da441b9a6 Signed-off-by: mydongistiny (cherry picked from commit 6ffe0a3dbff9aaa837504be23206b5f518d8efcc) --- .../android/internal/app/IBatteryStats.aidl | 3 +++ .../internal/os/BatteryStatsHelper.java | 19 +++++++++++++++++++ core/res/AndroidManifest.xml | 5 +++++ data/etc/privapp-permissions-platform.xml | 4 ++++ .../server/am/BatteryStatsService.java | 8 ++++++++ 5 files changed, 39 insertions(+) diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl index 15b1d75e88d..2c11b0ae331 100644 --- a/core/java/com/android/internal/app/IBatteryStats.aidl +++ b/core/java/com/android/internal/app/IBatteryStats.aidl @@ -161,4 +161,7 @@ interface IBatteryStats { /** {@hide} */ boolean setChargingStateUpdateDelayMillis(int delay); + + /** @hide **/ + void resetStatistics(); } diff --git a/core/java/com/android/internal/os/BatteryStatsHelper.java b/core/java/com/android/internal/os/BatteryStatsHelper.java index e85508ed789..6575979a1c4 100644 --- a/core/java/com/android/internal/os/BatteryStatsHelper.java +++ b/core/java/com/android/internal/os/BatteryStatsHelper.java @@ -263,6 +263,16 @@ public void clearStats() { mStats = null; } + private void clearAllStats() { + clearStats(); + sStatsXfer = null; + sBatteryBroadcastXfer = null; + for (File f : sFileXfer.keySet()) { + f.delete(); + } + sFileXfer.clear(); + } + @UnsupportedAppUsage public BatteryStats getStats() { if (mStats == null) { @@ -1053,6 +1063,15 @@ private void load() { } } + public void resetStatistics() { + try { + clearAllStats(); + mBatteryInfo.resetStatistics(); + } catch (RemoteException e) { + Log.e(TAG, "RemoteException:", e); + } + } + private static BatteryStatsImpl getStats(IBatteryStats service) { try { ParcelFileDescriptor pfd = service.getStatisticsStream(); diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 652d73abad2..b04cc894dff 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -3911,6 +3911,11 @@ + + + diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index ab839efe1a4..9d3d704d032 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -240,6 +240,10 @@ applications that come with the platform + + + + diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index a47ea4f95b3..19a27c7321e 100644 --- a/services/core/java/com/android/server/am/BatteryStatsService.java +++ b/services/core/java/com/android/server/am/BatteryStatsService.java @@ -428,6 +428,14 @@ public boolean isCharging() { } } + public void resetStatistics() { + mContext.enforceCallingPermission( + android.Manifest.permission.RESET_BATTERY_STATS, null); + synchronized (mStats) { + mStats.resetAllStatsCmdLocked(); + } + } + public long computeBatteryTimeRemaining() { synchronized (mStats) { long time = mStats.computeBatteryTimeRemaining(SystemClock.elapsedRealtime()); From d6626cb1c88948d31664576afe98123d56a117f1 Mon Sep 17 00:00:00 2001 From: DennySPB Date: Wed, 26 Jun 2019 10:37:44 +0300 Subject: [PATCH 25/26] Smart Charging: add reset battery stats option [1/2] When charging level reach user defined level allow to reset battery stats Change-Id: I5ee7ac164d7db2cb39544b671ce167fff378c647 --- core/java/android/provider/Settings.java | 5 +++++ core/res/AndroidManifest.xml | 5 ----- data/etc/privapp-permissions-platform.xml | 4 ---- .../android/server/am/BatteryStatsService.java | 2 -- .../server/power/PowerManagerService.java | 17 +++++++++++++++++ 5 files changed, 22 insertions(+), 11 deletions(-) diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 719585abe59..d7d9921e916 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -4812,6 +4812,11 @@ public boolean validate(@Nullable String value) { */ public static final String SMART_CHARGING = "smart_charging"; + /** + * @hide + */ + public static final String SMART_CHARGING_RESET_STATS = "smart_charging_reset_stats"; + /** * @hide */ diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index b04cc894dff..652d73abad2 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -3911,11 +3911,6 @@ - - - diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index 9d3d704d032..ab839efe1a4 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -240,10 +240,6 @@ applications that come with the platform - - - - diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index 19a27c7321e..f0ac573fe73 100644 --- a/services/core/java/com/android/server/am/BatteryStatsService.java +++ b/services/core/java/com/android/server/am/BatteryStatsService.java @@ -429,8 +429,6 @@ public boolean isCharging() { } public void resetStatistics() { - mContext.enforceCallingPermission( - android.Manifest.permission.RESET_BATTERY_STATS, null); synchronized (mStats) { mStats.resetAllStatsCmdLocked(); } diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index 7c5dbe7224e..27dfa9e82fc 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -592,6 +592,7 @@ public ProfilePowerState(@UserIdInt int userId, long screenOffTimeout) { // Smart charging private boolean mSmartChargingEnabled; + private boolean mSmartChargingResetStats; private boolean mPowerInputSuspended = false; private int mSmartChargingLevel; private int mSmartChargingResumeLevel; @@ -941,6 +942,9 @@ public void systemReady(IAppOpsService appOps) { resolver.registerContentObserver(Settings.System.getUriFor( Settings.System.SMART_CHARGING_RESUME_LEVEL), false, mSettingsObserver, UserHandle.USER_ALL); + resolver.registerContentObserver(Settings.System.getUriFor( + Settings.System.SMART_CHARGING_RESET_STATS), + false, mSettingsObserver, UserHandle.USER_ALL); IVrManager vrManager = IVrManager.Stub.asInterface(getBinderService(Context.VR_SERVICE)); if (vrManager != null) { @@ -1024,6 +1028,8 @@ void readConfigurationLocked() { com.android.internal.R.string.config_SmartChargingSuspendValue); mPowerInputResumeValue = resources.getString( com.android.internal.R.string.config_SmartChargingResumeValue); + mSmartChargingResetStats = Settings.System.getInt(mContext.getContentResolver(), + Settings.System.SMART_CHARGING_RESET_STATS, 0) == 1; } private void updateSettingsLocked() { @@ -1060,6 +1066,8 @@ private void updateSettingsLocked() { mSmartChargingResumeLevel = Settings.System.getInt(resolver, Settings.System.SMART_CHARGING_RESUME_LEVEL, mSmartChargingResumeLevelDefaultConfig); + mSmartChargingResetStats = Settings.System.getInt(resolver, + Settings.System.SMART_CHARGING_RESET_STATS, 0) == 1; if (mSupportsDoubleTapWakeConfig) { boolean doubleTapWakeEnabled = Settings.Secure.getIntForUser(resolver, @@ -1864,6 +1872,15 @@ private void updateSmartChargingStatus() { } if (mSmartChargingEnabled && !mPowerInputSuspended && (mBatteryLevel >= mSmartChargingLevel)) { + Slog.i(TAG, "Smart charging reset stats: " + mSmartChargingResetStats); + if (mSmartChargingResetStats) { + try { + mBatteryStats.resetStatistics(); + } catch (RemoteException e) { + Slog.e(TAG, "failed to reset battery statistics"); + } + } + try { FileUtils.stringToFile(mPowerInputSuspendSysfsNode, mPowerInputSuspendValue); mPowerInputSuspended = true; From 76f1a737df8c2dc1cd9510758bfc43e77f7d4eab Mon Sep 17 00:00:00 2001 From: Danny Lin Date: Sun, 13 Dec 2020 19:03:05 -0800 Subject: [PATCH 26/26] base: Grant battery stats reset permission to Settings Change-Id: Iad153e0553ef076568537a51bd0d2855b27dd5ee --- data/etc/com.android.settings.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/data/etc/com.android.settings.xml b/data/etc/com.android.settings.xml index 3e53a383854..4172f69e516 100644 --- a/data/etc/com.android.settings.xml +++ b/data/etc/com.android.settings.xml @@ -20,6 +20,7 @@ +