Merge "Add EuiccManager#retainSubscriptionsOnFactoryReset API." into oc-dr1-dev
diff --git a/Android.mk b/Android.mk
index 933ac62..55ea69a 100644
--- a/Android.mk
+++ b/Android.mk
@@ -292,6 +292,7 @@
 	core/java/android/service/euicc/IGetEidCallback.aidl \
 	core/java/android/service/euicc/IGetEuiccInfoCallback.aidl \
 	core/java/android/service/euicc/IGetEuiccProfileInfoListCallback.aidl \
+	core/java/android/service/euicc/IRetainSubscriptionsForFactoryResetCallback.aidl \
 	core/java/android/service/euicc/ISwitchToSubscriptionCallback.aidl \
 	core/java/android/service/euicc/IUpdateSubscriptionNicknameCallback.aidl \
 	core/java/android/service/gatekeeper/IGateKeeperService.aidl \
diff --git a/core/java/android/service/euicc/EuiccService.java b/core/java/android/service/euicc/EuiccService.java
index 26f8528..0c2e4b7 100644
--- a/core/java/android/service/euicc/EuiccService.java
+++ b/core/java/android/service/euicc/EuiccService.java
@@ -317,6 +317,21 @@
     public abstract int onEraseSubscriptions(int slotId);
 
     /**
+     * Ensure that subscriptions will be retained on the next factory reset.
+     *
+     * <p>Called directly before a factory reset. Assumes that a normal factory reset will lead to
+     * profiles being erased on first boot (to cover fastboot/recovery wipes), so the implementation
+     * should persist some bit that will remain accessible after the factory reset to bypass this
+     * flow when this method is called.
+     *
+     * @param slotId ID of the SIM slot to use for the operation. This is currently not populated
+     *     but is here to future-proof the APIs.
+     * @return the result of the operation. May be one of the predefined {@code RESULT_} constants
+     *     or any implementation-specific code starting with {@link #RESULT_FIRST_USER}.
+     */
+    public abstract int onRetainSubscriptionsForFactoryReset(int slotId);
+
+    /**
      * Wrapper around IEuiccService that forwards calls to implementations of {@link EuiccService}.
      */
     private class IEuiccServiceWrapper extends IEuiccService.Stub {
@@ -488,5 +503,21 @@
                 }
             });
         }
+
+        @Override
+        public void retainSubscriptionsForFactoryReset(int slotId,
+                IRetainSubscriptionsForFactoryResetCallback callback) {
+            mExecutor.execute(new Runnable() {
+                @Override
+                public void run() {
+                    int result = EuiccService.this.onRetainSubscriptionsForFactoryReset(slotId);
+                    try {
+                        callback.onComplete(result);
+                    } catch (RemoteException e) {
+                        // Can't communicate with the phone process; ignore.
+                    }
+                }
+            });
+        }
     }
 }
diff --git a/core/java/android/service/euicc/IEuiccService.aidl b/core/java/android/service/euicc/IEuiccService.aidl
index 18ea915..e10dd8c 100644
--- a/core/java/android/service/euicc/IEuiccService.aidl
+++ b/core/java/android/service/euicc/IEuiccService.aidl
@@ -24,6 +24,7 @@
 import android.service.euicc.IGetEidCallback;
 import android.service.euicc.IGetEuiccInfoCallback;
 import android.service.euicc.IGetEuiccProfileInfoListCallback;
+import android.service.euicc.IRetainSubscriptionsForFactoryResetCallback;
 import android.service.euicc.ISwitchToSubscriptionCallback;
 import android.service.euicc.IUpdateSubscriptionNicknameCallback;
 import android.telephony.euicc.DownloadableSubscription;
@@ -46,4 +47,6 @@
     void updateSubscriptionNickname(int slotId, String iccid, String nickname,
             in IUpdateSubscriptionNicknameCallback callback);
     void eraseSubscriptions(int slotId, in IEraseSubscriptionsCallback callback);
+    void retainSubscriptionsForFactoryReset(
+            int slotId, in IRetainSubscriptionsForFactoryResetCallback callback);
 }
\ No newline at end of file
diff --git a/core/java/android/service/euicc/IRetainSubscriptionsForFactoryResetCallback.aidl b/core/java/android/service/euicc/IRetainSubscriptionsForFactoryResetCallback.aidl
new file mode 100644
index 0000000..1276830
--- /dev/null
+++ b/core/java/android/service/euicc/IRetainSubscriptionsForFactoryResetCallback.aidl
@@ -0,0 +1,22 @@
+/*
+ * 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 android.service.euicc;
+
+/** @hide */
+oneway interface IRetainSubscriptionsForFactoryResetCallback {
+    void onComplete(int result);
+}
\ No newline at end of file
diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java
index f22a632..8304d84 100644
--- a/telephony/java/android/telephony/euicc/EuiccManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccManager.java
@@ -475,6 +475,36 @@
         }
     }
 
+    /**
+     * Ensure that subscriptions will be retained on the next factory reset.
+     *
+     * <p>By default, all subscriptions on the eUICC are erased the first time a device boots (ever
+     * and after factory resets). This ensures that the data is wiped after a factory reset is
+     * performed via fastboot or recovery mode, as these modes do not support the necessary radio
+     * communication needed to wipe the eSIM.
+     *
+     * <p>However, this method may be called right before a factory reset issued via settings when
+     * the user elects to retain subscriptions. Doing so will mark them for retention so that they
+     * are not cleared after the ensuing reset.
+     *
+     * <p>Requires that the calling app has the {@link android.Manifest.permission#MASTER_CLEAR}
+     * permission. This is for internal system use only.
+     *
+     * @param callbackIntent a PendingIntent to launch when the operation completes.
+     * @hide
+     */
+    public void retainSubscriptionsForFactoryReset(PendingIntent callbackIntent) {
+        if (!isEnabled()) {
+            sendUnavailableError(callbackIntent);
+            return;
+        }
+        try {
+            mController.retainSubscriptionsForFactoryReset(callbackIntent);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
     private static void sendUnavailableError(PendingIntent callbackIntent) {
         try {
             callbackIntent.send(EMBEDDED_SUBSCRIPTION_RESULT_ERROR);
diff --git a/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl b/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl
index fa43631..b3fc90d 100644
--- a/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl
@@ -40,4 +40,5 @@
     oneway void updateSubscriptionNickname(int subscriptionId, String nickname,
         in PendingIntent callbackIntent);
     oneway void eraseSubscriptions(in PendingIntent callbackIntent);
+    oneway void retainSubscriptionsForFactoryReset(in PendingIntent callbackIntent);
 }
\ No newline at end of file