Merge "Set verified stamps to be trusted" into rvc-dev
diff --git a/packages/SystemUI/res/layout/controls_dialog_pin.xml b/packages/SystemUI/res/layout/controls_dialog_pin.xml
new file mode 100644
index 0000000..b77d6fa
--- /dev/null
+++ b/packages/SystemUI/res/layout/controls_dialog_pin.xml
@@ -0,0 +1,36 @@
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical"
+    android:paddingLeft="?android:attr/dialogPreferredPadding"
+    android:paddingRight="?android:attr/dialogPreferredPadding">
+  <EditText
+      android:id="@+id/controls_pin_input"
+      android:layout_width="match_parent"
+      android:layout_height="wrap_content"
+      android:hint="@string/controls_pin_instructions"
+      android:inputType="numberPassword" />
+  <CheckBox
+      android:id="@+id/controls_pin_use_alpha"
+      android:layout_width="match_parent"
+      android:layout_height="wrap_content"
+      android:layout_marginTop="5dp"
+      android:text="@string/controls_pin_use_alphanumeric" />
+</LinearLayout>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 5b28479..caf22fe 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2646,4 +2646,11 @@
     <string name="controls_dialog_ok">Add to favorites</string>
     <!-- Controls dialog message [CHAR LIMIT=NONE] -->
     <string name="controls_dialog_message"><xliff:g id="app" example="System UI">%s</xliff:g> suggested this control to add to your favorites.</string>
+
+    <!-- Controls PIN entry dialog, switch to alphanumeric keyboard [CHAR LIMIT=100] -->
+    <string name="controls_pin_use_alphanumeric">PIN contains letters or symbols</string>
+    <!-- Controls PIN entry dialog, title [CHAR LIMIT=30] -->
+    <string name="controls_pin_verify">Verify device PIN</string>
+    <!-- Controls PIN entry dialog, text hint [CHAR LIMIT=30] -->
+    <string name="controls_pin_instructions">Enter PIN</string>
 </resources>
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ChallengeDialogs.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ChallengeDialogs.kt
new file mode 100644
index 0000000..2494fd1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ChallengeDialogs.kt
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.controls.ui
+
+import android.app.AlertDialog
+import android.app.Dialog
+import android.content.DialogInterface
+import android.service.controls.actions.BooleanAction
+import android.service.controls.actions.CommandAction
+import android.service.controls.actions.ControlAction
+import android.service.controls.actions.FloatAction
+import android.service.controls.actions.ModeAction
+import android.text.InputType
+import android.util.Log
+import android.view.WindowManager
+import android.widget.CheckBox
+import android.widget.EditText
+
+import com.android.systemui.R
+
+/**
+ * Creates all dialogs for challengeValues that can occur from a call to
+ * {@link ControlsProviderService#performControlAction}. The types of challenge
+ * responses are listed in {@link ControlAction.ResponseResult}.
+ */
+object ChallengeDialogs {
+
+    fun createPinDialog(cvh: ControlViewHolder): Dialog? {
+        val lastAction = cvh.lastAction
+        if (lastAction == null) {
+            Log.e(ControlsUiController.TAG,
+                "PIN Dialog attempted but no last action is set. Will not show")
+            return null
+        }
+        val builder = AlertDialog.Builder(
+            cvh.context,
+            android.R.style.Theme_DeviceDefault_Dialog_Alert
+        ).apply {
+            setTitle(R.string.controls_pin_verify)
+            setView(R.layout.controls_dialog_pin)
+            setPositiveButton(
+                android.R.string.ok,
+                DialogInterface.OnClickListener { dialog, _ ->
+                    if (dialog is Dialog) {
+                        dialog.requireViewById<EditText>(R.id.controls_pin_input)
+                        val pin = dialog.requireViewById<EditText>(R.id.controls_pin_input)
+                            .getText().toString()
+                        cvh.action(addChallengeValue(lastAction, pin))
+                        dialog.dismiss()
+                    }
+            })
+            setNegativeButton(
+                android.R.string.cancel,
+                DialogInterface.OnClickListener { dialog, _ -> dialog.cancel() }
+            )
+        }
+        return builder.create().apply {
+            getWindow().apply {
+                setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY)
+                setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE)
+            }
+            setOnShowListener(DialogInterface.OnShowListener { _ ->
+                val editText = requireViewById<EditText>(R.id.controls_pin_input)
+                requireViewById<CheckBox>(R.id.controls_pin_use_alpha).setOnClickListener { v ->
+                    if ((v as CheckBox).isChecked) {
+                        editText.setInputType(
+                            InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_PASSWORD)
+                    } else {
+                        editText.setInputType(
+                            InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_VARIATION_PASSWORD)
+                    }
+                }
+                editText.requestFocus()
+            })
+        }
+    }
+
+    private fun addChallengeValue(action: ControlAction, challengeValue: String): ControlAction {
+        val id = action.getTemplateId()
+        return when (action) {
+            is BooleanAction -> BooleanAction(id, action.getNewState(), challengeValue)
+            is FloatAction -> FloatAction(id, action.getNewValue(), challengeValue)
+            is CommandAction -> CommandAction(id, challengeValue)
+            is ModeAction -> ModeAction(id, action.getNewMode(), challengeValue)
+            else -> throw IllegalStateException("'action' is not a known type: $action")
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
index d56428d..b1b98bc 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
@@ -27,7 +27,6 @@
 import android.service.controls.templates.TemperatureControlTemplate
 import android.service.controls.templates.ToggleRangeTemplate
 import android.service.controls.templates.ToggleTemplate
-import android.util.Log
 import android.view.View
 import android.view.ViewGroup
 import android.widget.ImageView
@@ -57,6 +56,7 @@
     lateinit var cws: ControlWithState
     var cancelUpdate: Runnable? = null
     var behavior: Behavior? = null
+    var lastAction: ControlAction? = null
 
     init {
         val ld = layout.getBackground() as LayerDrawable
@@ -98,7 +98,6 @@
 
     fun actionResponse(@ControlAction.ResponseResult response: Int) {
         // TODO: b/150931809 - handle response codes
-        Log.d(ControlsUiController.TAG, "Received response code: $response")
     }
 
     fun setTransientStatus(tempStatus: String) {
@@ -115,6 +114,7 @@
     }
 
     fun action(action: ControlAction) {
+        lastAction = action
         controlsController.action(cws.componentName, cws.ci, action)
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
index eaf57ff..ca6619b 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
@@ -18,6 +18,7 @@
 
 import android.accounts.Account
 import android.accounts.AccountManager
+import android.app.Dialog
 import android.content.ComponentName
 import android.content.Context
 import android.content.Intent
@@ -28,6 +29,7 @@
 import android.os.IBinder
 import android.service.controls.Control
 import android.service.controls.TokenProvider
+import android.service.controls.actions.ControlAction
 import android.util.Log
 import android.view.ContextThemeWrapper
 import android.view.LayoutInflater
@@ -49,8 +51,8 @@
 import com.android.systemui.controls.management.ControlsProviderSelectorActivity
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.R
 import com.android.systemui.util.concurrency.DelayableExecutor
+import com.android.systemui.R
 
 import dagger.Lazy
 
@@ -152,7 +154,7 @@
     private lateinit var parent: ViewGroup
     private lateinit var lastItems: List<SelectionItem>
     private var popup: ListPopupWindow? = null
-
+    private var activeDialog: Dialog? = null
     private val addControlsItem: SelectionItem
 
     init {
@@ -388,6 +390,7 @@
     override fun hide() {
         Log.d(ControlsUiController.TAG, "hide()")
         popup?.dismiss()
+        activeDialog?.dismiss()
 
         controlsController.get().unsubscribe()
         context.unbindService(tokenProviderConnection)
@@ -418,7 +421,15 @@
     override fun onActionResponse(componentName: ComponentName, controlId: String, response: Int) {
         val key = ControlKey(componentName, controlId)
         uiExecutor.execute {
-            controlViewsById.get(key)?.actionResponse(response)
+            controlViewsById.get(key)?.let { cvh ->
+                when (response) {
+                    ControlAction.RESPONSE_CHALLENGE_PIN -> {
+                        activeDialog = ChallengeDialogs.createPinDialog(cvh)
+                        activeDialog?.show()
+                    }
+                    else -> cvh.actionResponse(response)
+                }
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 968528c..7c3cab1 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -951,18 +951,18 @@
                 || isVpnServicePreConsented(context, packageName);
     }
 
-    private int getAppUid(String app, int userHandle) {
+    private int getAppUid(final String app, final int userHandle) {
         if (VpnConfig.LEGACY_VPN.equals(app)) {
             return Process.myUid();
         }
         PackageManager pm = mContext.getPackageManager();
-        int result;
-        try {
-            result = pm.getPackageUidAsUser(app, userHandle);
-        } catch (NameNotFoundException e) {
-            result = -1;
-        }
-        return result;
+        return Binder.withCleanCallingIdentity(() -> {
+            try {
+                return pm.getPackageUidAsUser(app, userHandle);
+            } catch (NameNotFoundException e) {
+                return -1;
+            }
+        });
     }
 
     private boolean doesPackageTargetAtLeastQ(String packageName) {
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index 7dd2e55..83fe556 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -331,7 +331,8 @@
     }
 
     // Reverts apex sessions and user data (if checkpoint is supported). Also reboots the device.
-    private void abortCheckpoint() {
+    private void abortCheckpoint(String errorMsg) {
+        Slog.e(TAG, "Aborting checkpoint: " + errorMsg);
         try {
             if (supportsCheckpoint() && needsCheckpoint()) {
                 mApexManager.revertActiveSessions();
@@ -504,6 +505,8 @@
             // mode. If not, we fail all sessions.
             if (supportsCheckpoint() && !needsCheckpoint()) {
                 // TODO(b/146343545): Persist failure reason across checkpoint reboot
+                Slog.d(TAG, "Reverting back to safe state. Marking " + session.sessionId
+                        + " as failed.");
                 session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_UNKNOWN,
                         "Reverting back to safe state");
                 return;
@@ -524,26 +527,29 @@
 
         if (hasApex) {
             if (apexSessionInfo == null) {
+                String errorMsg = "apexd did not know anything about a staged session supposed to"
+                        + " be activated";
                 session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED,
-                        "apexd did not know anything about a staged session supposed to be"
-                        + "activated");
-                abortCheckpoint();
+                        errorMsg);
+                abortCheckpoint(errorMsg);
                 return;
             }
             if (isApexSessionFailed(apexSessionInfo)) {
+                String errorMsg = "APEX activation failed. Check logcat messages from apexd for "
+                        + "more information.";
                 session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED,
-                        "APEX activation failed. Check logcat messages from apexd for "
-                                + "more information.");
-                abortCheckpoint();
+                        errorMsg);
+                abortCheckpoint(errorMsg);
                 return;
             }
             if (!apexSessionInfo.isActivated && !apexSessionInfo.isSuccess) {
                 // Apexd did not apply the session for some unknown reason. There is no guarantee
                 // that apexd will install it next time. Safer to proactively mark as failed.
+                String errorMsg = "Staged session " + session.sessionId + "at boot didn't "
+                        + "activate nor fail. Marking it as failed anyway.";
                 session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED,
-                        "Staged session " + session.sessionId + "at boot didn't "
-                                + "activate nor fail. Marking it as failed anyway.");
-                abortCheckpoint();
+                        errorMsg);
+                abortCheckpoint(errorMsg);
                 return;
             }
             snapshotAndRestoreForApexSession(session);
@@ -556,7 +562,7 @@
             installApksInSession(session);
         } catch (PackageManagerException e) {
             session.setStagedSessionFailed(e.error, e.getMessage());
-            abortCheckpoint();
+            abortCheckpoint(e.getMessage());
 
             // If checkpoint is not supported, we have to handle failure for one staged session.
             if (!hasApex) {