summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Dan Sandler <dsandler@android.com> 2021-03-23 23:38:00 -0400
committer Daniel Sandler <dsandler@android.com> 2021-03-29 20:25:01 +0000
commitf22cf3df132f50e7f949b0e6a90ec01acf8328f2 (patch)
treec033b322577db18a7857706888e2dce542f0e9fb
parent0f97905040606c3bba2f90d9dba33c16cb76873a (diff)
Allow explicitly setting the camera component launched by double-press power.
Using an implicit intent at the moment of picture-taking usually goes unnoticed. But immediately after installing a new camera, this behavior becomes incredibly frustrating to users as they are presented with a puzzling resolver dialog (or in the case of the secure camera, the authenticator). And if, at this moment, the user chooses to make one of the options a default, it's almost impossible to figure out how to change this setting. As a result, many OEMs simply hardcode the camera gesture to launch a specific preinstalled camera, but this is poorly supported by AOSP, leading to duplicate implementations and bugs. This patch routes all camera intents in System UI through a single utility class, creating a convenient spot to insert a resource that contains the OEM's default preinstalled camera app. Note that this does not affect implicit intent resolution in any way; any app may create a chooser for, e.g., MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA and allow the user to pick from the available cameras. Bugreport/dumpsys output to look for: $ adb shell dumpsys activity service com.android.systemui | grep -C3 'Camera gesture' | tail -3 Camera gesture intents: Insecure camera: Intent { act=android.media.action.STILL_IMAGE_CAMERA } Secure camera: Intent { act=android.media.action.STILL_IMAGE_CAMERA_SECURE flg=0x800000 } Override package: null Bug: 171807357 Fixes: 154218868 Test: atest SystemUITests Change-Id: I2c0033e52c8a3963768d29f2e76e555d405aaa7e
-rw-r--r--packages/SystemUI/docs/camera.md9
-rw-r--r--packages/SystemUI/res/values/config.xml4
-rw-r--r--packages/SystemUI/src/com/android/systemui/camera/CameraIntents.kt71
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java12
5 files changed, 97 insertions, 13 deletions
diff --git a/packages/SystemUI/docs/camera.md b/packages/SystemUI/docs/camera.md
index 7a7a5aa3eb7f..cabc65c53caa 100644
--- a/packages/SystemUI/docs/camera.md
+++ b/packages/SystemUI/docs/camera.md
@@ -13,8 +13,8 @@ _as of august 2020_
4. Inside SystemUI, [onCameraLaunchDetected](/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java#3927) looks at the keyguard state and determines
1. whether the camera is even allowed
2. whether the screen is on; if not, we need to delay until that happens
- 3. whether the device is locked (defined as "keyuguard is showing").
-5. If the device is unlocked (no keyguard), the camera is launched immediately. [Callsite](/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java#3949).
+ 3. whether the device is locked (defined as "keyguard is showing").
+5. If the device is unlocked (no keyguard), the camera is launched immediately. [Callsite in onCameraLaunchGestureDetected](/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java#4047).
6. If the keyguard is up, however, [KeyguardBottomAreaView.launchCamera](/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java#477) takes over to handle the "secure camera" (a different intent, usually directing to the same app, but giving that app the cue to not allow access to the photo roll, etc).
7. If the intent [would have to launch a resolver](/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java#480) (the user has multiple cameras installed and hasn’t chosen one to always launch for the `SECURE_CAMERA_INTENT`),
1. In order to show the resolver, the lockscreen "bouncer" (authentication method) [is first presented](/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java#523).
@@ -26,8 +26,9 @@ _as of august 2020_
* If the keyguard is not showing (device is unlocked)
- * `KeyguardBottomAreaView.INSECURE_CAMERA_INTENT`, defined to be `MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA`.
+ * `CameraIntents.getInsecureCameraIntent()`, defined to be `MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA`.
* [Callsite](/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java#3950) in StatusBar.java.
* If the keyguard is showing (device locked)
- * [KeyguardBottomAreaView.getCameraIntent()](/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java#366) is consulted, which allows the "keyguard right button" (which we don’t actually show) to control the camera intent. The [default implementation](/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java#831) returns one of `KeyguardBottomAreaView.INSECURE_CAMERA_INTENT` or `KeyguardBottomAreaView.SECURE_CAMERA_INTENT`, which are `MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA` and `MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE`, respectively.
+ * [KeyguardBottomAreaView.getCameraIntent()](/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java#366) is consulted, which allows the "keyguard right button" (which we don’t actually show) to control the camera intent. The [default implementation](/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java#831) returns one of `CameraIntents.getInsecureCameraIntent()` or `CameraIntents.getSecureCameraIntent()`, which are `MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA` and `MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE`, respectively.
* [Callsite](/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java#523) in KeyguardBottomAreaView.java.
+* Note that starting in Android 12, as required by some OEMs, if the special string resource `config_cameraGesturePackage` is nonempty, this will be treated as a package name to be added to the insecure camera intent, constraining the invocation to that single app and typically preventing implicit intent resolution. This package must be on the device or the camera gesture will no longer work properly. \ No newline at end of file
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 6e61148f4ac8..d37d2392d0b7 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -589,6 +589,10 @@
<!-- Determines whether the shell features all run on another thread. -->
<bool name="config_enableShellMainThread">false</bool>
+ <!-- package name of a built-in camera app to use to restrict implicit intent resolution
+ when the double-press power gesture is used. Ignored if empty. -->
+ <string translatable="false" name="config_cameraGesturePackage"></string>
+
<!-- Determines whether to allow the nav bar handle to be forced to be opaque. -->
<bool name="allow_force_nav_bar_handle_opaque">true</bool>
diff --git a/packages/SystemUI/src/com/android/systemui/camera/CameraIntents.kt b/packages/SystemUI/src/com/android/systemui/camera/CameraIntents.kt
new file mode 100644
index 000000000000..464bee18f030
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/camera/CameraIntents.kt
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.camera
+
+import android.content.Context
+import android.content.Intent
+import android.provider.MediaStore
+import android.text.TextUtils
+
+import com.android.systemui.R
+
+interface CameraIntents {
+ companion object {
+ const val DEFAULT_SECURE_CAMERA_INTENT_ACTION =
+ MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE
+ const val DEFAULT_INSECURE_CAMERA_INTENT_ACTION =
+ MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA
+
+ @JvmStatic
+ fun getOverrideCameraPackage(context: Context): String? {
+ context.resources.getString(R.string.config_cameraGesturePackage)?.let {
+ if (!TextUtils.isEmpty(it)) {
+ return it
+ }
+ }
+ return null
+ }
+
+ @JvmStatic
+ fun getInsecureCameraIntent(context: Context): Intent {
+ val intent = Intent(DEFAULT_INSECURE_CAMERA_INTENT_ACTION)
+ getOverrideCameraPackage(context)?.let {
+ intent.setPackage(it)
+ }
+ return intent
+ }
+
+ @JvmStatic
+ fun getSecureCameraIntent(context: Context): Intent {
+ val intent = Intent(DEFAULT_SECURE_CAMERA_INTENT_ACTION)
+ getOverrideCameraPackage(context)?.let {
+ intent.setPackage(it)
+ }
+ return intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
+ }
+
+ @JvmStatic
+ fun isSecureCameraIntent(intent: Intent): Boolean {
+ return intent.getAction().equals(DEFAULT_SECURE_CAMERA_INTENT_ACTION)
+ }
+
+ @JvmStatic
+ fun isInsecureCameraIntent(intent: Intent): Boolean {
+ return intent.getAction().equals(DEFAULT_INSECURE_CAMERA_INTENT_ACTION)
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index 80109cb7a06c..b891e6f4552c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -73,6 +73,7 @@ import com.android.systemui.Dependency;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.assist.AssistManager;
+import com.android.systemui.camera.CameraIntents;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.IntentButtonProvider;
import com.android.systemui.plugins.IntentButtonProvider.IntentButton;
@@ -110,11 +111,6 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
private static final String RIGHT_BUTTON_PLUGIN
= "com.android.systemui.action.PLUGIN_LOCKSCREEN_RIGHT_BUTTON";
- private static final Intent SECURE_CAMERA_INTENT =
- new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE)
- .addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
- public static final Intent INSECURE_CAMERA_INTENT =
- new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA);
private static final Intent PHONE_INTENT = new Intent(Intent.ACTION_DIAL);
private static final int DOZE_ANIMATION_STAGGER_DELAY = 48;
private static final int DOZE_ANIMATION_ELEMENT_DURATION = 250;
@@ -498,7 +494,7 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
intent.putExtra(EXTRA_CAMERA_LAUNCH_SOURCE, source);
boolean wouldLaunchResolverActivity = mActivityIntentHelper.wouldLaunchResolverActivity(
intent, KeyguardUpdateMonitor.getCurrentUser());
- if (intent == SECURE_CAMERA_INTENT && !wouldLaunchResolverActivity) {
+ if (CameraIntents.isSecureCameraIntent(intent) && !wouldLaunchResolverActivity) {
AsyncTask.execute(new Runnable() {
@Override
public void run() {
@@ -855,7 +851,11 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
public Intent getIntent() {
boolean canDismissLs = mKeyguardStateController.canDismissLockScreen();
boolean secure = mKeyguardStateController.isMethodSecure();
- return (secure && !canDismissLs) ? SECURE_CAMERA_INTENT : INSECURE_CAMERA_INTENT;
+ if (secure && !canDismissLs) {
+ return CameraIntents.getSecureCameraIntent(getContext());
+ } else {
+ return CameraIntents.getInsecureCameraIntent(getContext());
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index d581c4b6f9b7..bac5a91c17ef 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -150,6 +150,7 @@ import com.android.systemui.SystemUI;
import com.android.systemui.accessibility.floatingmenu.AccessibilityFloatingMenuController;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.camera.CameraIntents;
import com.android.systemui.charging.WirelessChargingAnimation;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.colorextraction.SysuiColorExtractor;
@@ -2662,6 +2663,12 @@ public class StatusBar extends SystemUI implements DemoMode,
for (Map.Entry<String, ?> entry : Prefs.getAll(mContext).entrySet()) {
pw.print(" "); pw.print(entry.getKey()); pw.print("="); pw.println(entry.getValue());
}
+
+ pw.println("Camera gesture intents:");
+ pw.println(" Insecure camera: " + CameraIntents.getInsecureCameraIntent(mContext));
+ pw.println(" Secure camera: " + CameraIntents.getSecureCameraIntent(mContext));
+ pw.println(" Override package: "
+ + String.valueOf(CameraIntents.getOverrideCameraPackage(mContext)));
}
public static void dumpBarTransitions(PrintWriter pw, String var, BarTransitions transitions) {
@@ -2734,7 +2741,7 @@ public class StatusBar extends SystemUI implements DemoMode,
null /* remoteAnimation */));
options.setDisallowEnterPictureInPictureWhileLaunching(
disallowEnterPictureInPictureWhileLaunching);
- if (intent == KeyguardBottomAreaView.INSECURE_CAMERA_INTENT) {
+ if (CameraIntents.isInsecureCameraIntent(intent)) {
// Normally an activity will set it's requested rotation
// animation on its window. However when launching an activity
// causes the orientation to change this is too late. In these cases
@@ -4023,7 +4030,8 @@ public class StatusBar extends SystemUI implements DemoMode,
}
if (!mStatusBarKeyguardViewManager.isShowing()) {
- startActivityDismissingKeyguard(KeyguardBottomAreaView.INSECURE_CAMERA_INTENT,
+ final Intent cameraIntent = CameraIntents.getInsecureCameraIntent(mContext);
+ startActivityDismissingKeyguard(cameraIntent,
false /* onlyProvisioned */, true /* dismissShade */,
true /* disallowEnterPictureInPictureWhileLaunching */, null /* callback */, 0);
} else {