Merge "Implement dispatching of resolution UI."
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 2f031fa..85e2982 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -487,7 +487,7 @@
</service>
<!-- Handler for EuiccManager's public-facing intents. -->
- <activity android:name=".EuiccUiDispatcherActivity"
+ <activity android:name=".euicc.EuiccUiDispatcherActivity"
android:theme="@android:style/Theme.NoDisplay">
<!-- Max out priority to ensure nobody else will handle these intents. -->
<intent-filter android:priority="1000">
@@ -499,6 +499,21 @@
</intent-filter>
</activity>
+ <!--
+ Handler for EuiccManager's resolution intents. These are locked down so that only
+ privileged processes can start them, which means we can trust the Intent used to start
+ it (which contains a description of the next step to perform after resolution).
+ -->
+ <activity android:name=".euicc.EuiccResolutionUiDispatcherActivity"
+ android:permission="android.permission.CALL_PRIVILEGED">
+ <!-- Max out priority to ensure nobody else will handle these intents. -->
+ <intent-filter android:priority="1000">
+ <action android:name=
+ "android.telephony.euicc.action.RESOLVE_ERROR" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+
<activity android:name="EmergencyCallbackModeExitDialog"
android:excludeFromRecents="true"
android:label="@string/ecm_exit_dialog"
diff --git a/src/com/android/phone/euicc/EuiccResolutionUiDispatcherActivity.java b/src/com/android/phone/euicc/EuiccResolutionUiDispatcherActivity.java
new file mode 100644
index 0000000..6f28bbf
--- /dev/null
+++ b/src/com/android/phone/euicc/EuiccResolutionUiDispatcherActivity.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.phone.euicc;
+
+import android.annotation.Nullable;
+import android.content.Intent;
+import android.service.euicc.EuiccService;
+import android.telephony.euicc.EuiccManager;
+import android.util.Log;
+
+/**
+ * Trampoline activity to forward eUICC intents for error resolutions to the active UI
+ * implementation.
+ *
+ * <p>Unlike {@link EuiccUiDispatcherActivity}, this activity is started with extras that must not
+ * be tampered with, because they are used to resume the operation after the error is resolved. We
+ * thus declare it as a separate activity which requires a locked-down permission to start.
+ */
+public class EuiccResolutionUiDispatcherActivity extends EuiccUiDispatcherActivity {
+ private static final String TAG = "EuiccResUiDispatcher";
+
+ @Override
+ @Nullable
+ protected Intent getEuiccUiIntent() {
+ String action = getIntent().getAction();
+ if (!EuiccManager.ACTION_RESOLVE_ERROR.equals(action)) {
+ Log.w(TAG, "Unsupported action: " + action);
+ return null;
+ }
+
+ String euiccUiAction =
+ getIntent().getStringExtra(
+ EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_RESOLUTION_ACTION);
+ if (!EuiccService.RESOLUTION_ACTIONS.contains(euiccUiAction)) {
+ Log.w(TAG, "Unknown resolution action: " + euiccUiAction);
+ return null;
+ }
+
+ Intent euiccUiIntent = new Intent(euiccUiAction);
+ // Propagate the extras from the original Intent.
+ euiccUiIntent.putExtras(getIntent());
+ return euiccUiIntent;
+ }
+}
diff --git a/src/com/android/phone/EuiccUiDispatcherActivity.java b/src/com/android/phone/euicc/EuiccUiDispatcherActivity.java
similarity index 78%
rename from src/com/android/phone/EuiccUiDispatcherActivity.java
rename to src/com/android/phone/euicc/EuiccUiDispatcherActivity.java
index eaa3885..d96befa 100644
--- a/src/com/android/phone/EuiccUiDispatcherActivity.java
+++ b/src/com/android/phone/euicc/EuiccUiDispatcherActivity.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.phone;
+package com.android.phone.euicc;
import android.annotation.Nullable;
import android.app.Activity;
@@ -37,35 +37,54 @@
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
try {
- Intent euiccUiIntent = getEuiccUiIntent();
- if (euiccUiIntent != null) {
- euiccUiIntent.setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
- startActivity(euiccUiIntent);
- } else {
+ Intent euiccUiIntent = resolveEuiccUiIntent();
+ if (euiccUiIntent == null) {
setResult(RESULT_CANCELED);
+ return;
}
+
+ euiccUiIntent.setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
+ startActivity(euiccUiIntent);
} finally {
// Since we're using Theme.NO_DISPLAY, we must always finish() at the end of onCreate().
finish();
}
}
- /**
- * Return a resolved Intent to start the Euicc app's UI for the given intent, or null if the
- * implementation couldn't be resolved.
- */
@VisibleForTesting
@Nullable
- Intent getEuiccUiIntent() {
- String action = getIntent().getAction();
-
+ Intent resolveEuiccUiIntent() {
EuiccManager euiccManager = (EuiccManager) getSystemService(Context.EUICC_SERVICE);
if (!euiccManager.isEnabled()) {
- Log.w(TAG, "eUICC is not enabled; cannot start activity for action: " + action);
+ setResult(RESULT_CANCELED);
return null;
}
- final String euiccUiAction;
+ Intent euiccUiIntent = getEuiccUiIntent();
+ if (euiccUiIntent == null) {
+ Log.w(TAG, "Unable to handle intent");
+ return null;
+ }
+
+ ActivityInfo activityInfo = findBestActivity(euiccUiIntent);
+ if (activityInfo == null) {
+ Log.w(TAG, "Could not resolve activity for intent: " + euiccUiIntent);
+ return null;
+ }
+
+ euiccUiIntent.setComponent(activityInfo.getComponentName());
+ return euiccUiIntent;
+ }
+
+ /**
+ * Return an Intent to start the Euicc app's UI for the given intent, or null if given intent
+ * cannot be handled.
+ */
+ @Nullable
+ protected Intent getEuiccUiIntent() {
+ String action = getIntent().getAction();
+
+ String euiccUiAction;
switch (action) {
case EuiccManager.ACTION_MANAGE_EMBEDDED_SUBSCRIPTIONS:
euiccUiAction = EuiccService.ACTION_MANAGE_EMBEDDED_SUBSCRIPTIONS;
@@ -82,15 +101,7 @@
return null;
}
- Intent euiccUiIntent = new Intent(euiccUiAction);
- ActivityInfo activityInfo = findBestActivity(euiccUiIntent);
-
- if (activityInfo == null) {
- Log.w(TAG, "Could not resolve activity for action: " + euiccUiAction);
- return null;
- }
- euiccUiIntent.setComponent(activityInfo.getComponentName());
- return euiccUiIntent;
+ return new Intent(euiccUiAction);
}
@VisibleForTesting
diff --git a/tests/src/com/android/phone/EuiccUiDispatcherActivityTest.java b/tests/src/com/android/phone/euicc/EuiccUiDispatcherActivityTest.java
similarity index 81%
rename from tests/src/com/android/phone/EuiccUiDispatcherActivityTest.java
rename to tests/src/com/android/phone/euicc/EuiccUiDispatcherActivityTest.java
index 3385dc3..722e1bd 100644
--- a/tests/src/com/android/phone/EuiccUiDispatcherActivityTest.java
+++ b/tests/src/com/android/phone/euicc/EuiccUiDispatcherActivityTest.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.phone;
+package com.android.phone.euicc;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
@@ -68,38 +68,38 @@
}
@Test
- public void testGetEuiccUiIntent_disabled() {
+ public void testResolveEuiccUiIntent_disabled() {
when(mMockEuiccManager.isEnabled()).thenReturn(false);
- assertNull(mActivity.getEuiccUiIntent());
+ assertNull(mActivity.resolveEuiccUiIntent());
}
@Test
- public void testGetEuiccIntent_unsupportedAction() {
+ public void testResolveEuiccUiIntent_unsupportedAction() {
mIntent = new Intent("fake.action");
- assertNull(mActivity.getEuiccUiIntent());
+ assertNull(mActivity.resolveEuiccUiIntent());
}
@Test
- public void testGetEuiccIntent_alreadyProvisioned() {
+ public void testResolveEuiccUiIntent_alreadyProvisioned() {
mIntent = PROVISION_INTENT;
- assertNull(mActivity.getEuiccUiIntent());
+ assertNull(mActivity.resolveEuiccUiIntent());
}
@Test
- public void testGetEuiccIntent_noImplementation() {
+ public void testResolveEuiccUiIntent_noImplementation() {
mActivityInfo = null;
- assertNull(mActivity.getEuiccUiIntent());
+ assertNull(mActivity.resolveEuiccUiIntent());
}
@Test
- public void testGetEuiccIntent_validManage() {
- assertNotNull(mActivity.getEuiccUiIntent());
+ public void testResolveEuiccUiIntent_validManage() {
+ assertNotNull(mActivity.resolveEuiccUiIntent());
}
@Test
- public void testGetEuiccIntent_validProvision() {
+ public void testResolveEuiccUiIntent_validProvision() {
mIsProvisioned = false;
- assertNotNull(mActivity.getEuiccUiIntent());
+ assertNotNull(mActivity.resolveEuiccUiIntent());
}
class TestEuiccUiDispatcherActivity extends EuiccUiDispatcherActivity {